Σχεδόν παίζει αλλά δεν χρειάζεται! Η ευκολότερη υλοποίηση είναι να ορίζεις και να δημιουργείς το instance σαν static μεταβλητή. Το πρόβλημα στη C++ είναι ότι η σειρά με την οποία δημιουργούνται τα global αντικείμενα δεν εξασφαλισμένη. Αυτό σημαίνει ότι αν ένα Singleton χρησιμοποιεί ένα άλλο Singleton, μπορεί να υπάρξουν λάθη.
Τόσο στη C# όσο και στη VB.NET μια static(shared) μεταβλητή γίνεται initialize οπωσδήποτε πριν την χρησιμοποιήσει ο οποιοσδήποτε. Στο
specification της VB αναφέρει
For Shared variables, variable initializers correspond to assignment statements that are executed after the program begins, but before the Shared variable is first referenced. (§9.6.3 Variable Initializers)
Έτσι άνετα μπορείς να γράψεις το singleton ως:
| | public sealed class Singleton { static readonly Singleton instance=new Singleton();
static Singleton() { } Singleton() { } public static Singleton Instance { get { return instance; } } } |
Το instance θα δημιουργηθεί την πρώτη φορά που θα κληθεί κάποιο static member του Singleton. Επειδή εδώ μόνο το Instance() μπορεί να κληθεί, το lazy instantiation παίζει τέλεια. Αν πρόσθετες και άλλα static members, το πράγμα θα ήταν πιο αβέβαιο.
Δες και ένα πολύ καλό άρθρο Implementing the Singleton Pattern in C#, το οποίο αναλύει διεξοδικότατα το Singleton.
Να εξηγήσω τί σημαίνει το σχεδόν παίζει για το double-checked locking. Το double-checked locking παίζει αν υπάρχει μόνο ένας επεξεργαστής. Διαφορετικά τίποτα δεν εμποδίζει δύο threads, σε δύο διαφορετικούς επεξεργαστές να πιάσουν το ίδιο lock. Τουλάχιστον, το .NET δεν βάζει αυτό τον περιορισμό. Τον βάζουν όμως οι επεξεργαστές Intel. Αν η Intel αλλάξει ποτέ το memory model των επεξεργαστών το double-checked locking θα αποτύχει.
Αυτό δεν είναι τόσο απίθανο όσο ακούγεται. Μπορεί π.χ. να βγάλει η Intel ή η AMD ένα νέο dual-core τσιπάκι, με δύο mode λειτουργίας, συμβατό και Turbo με χαλαρότερο memory model για καλύτερες επιδόσεις.
Παναγιώτης Καναβός, Freelancer
Twitter: http://www.twitter.com/pkanavos