public sealed class Singleton
{
Singleton() {}
public static Singleton Instance
{
get
{
return Nested.instance;
}
}
class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested() {}
internal static readonly Singleton instance = new Singleton();
}
}
Chciałbym wdrożyć wzór Jona Skeeta Singletona w mojej bieżącej aplikacji w języku C #.
Mam dwie wątpliwości co do kodu
Jak można uzyskać dostęp do zewnętrznej klasy wewnątrz klasy zagnieżdżonej? mam na myśli
internal static readonly Singleton instance = new Singleton();
Czy coś nazywa się zamknięciem?
Nie mogę zrozumieć tego komentarza
// Explicit static constructor to tell C# compiler // not to mark type as beforefieldinit
co sugeruje ten komentarz?
c#
.net
design-patterns
architecture
singleton
amutha
źródło
źródło
Odpowiedzi:
Nie, to nie ma nic wspólnego z zamknięciami. Zagnieżdżona klasa ma dostęp do prywatnych członków swojej zewnętrznej klasy, w tym prywatnego konstruktora tutaj.
Przeczytaj mój artykuł na temat beforefieldinit . Możesz chcieć lub nie chcieć statycznego konstruktora no-op - zależy to od tego, jakie lenistwo gwarantuje, czego potrzebujesz. Należy pamiętać, że .NET 4 zmienia nieco semantykę inicjowania typu rzeczywistego (nadal w specyfikacji, ale bardziej leniwe niż wcześniej).
Czy naprawdę potrzebujesz tego wzoru? Czy na pewno nie możesz uciec od:
źródło
Lazy<T>
, abyś nie musiał deklarować statycznego konstruktora magicznegoBeforeFieldInit
efektu ubocznego?FieldBeforeInit
jestMahaBharata
odMicrosoft
Odnośnie do pytania (1): Odpowiedź Jona jest poprawna, ponieważ domyślnie zaznacza klasę „zagnieżdżoną” jako prywatną, nie podając jej do wiadomości publicznej ani wewnętrznej :-). Równie dobrze możesz to zrobić, dodając „prywatny”:
Odnośnie pytania (2): w zasadzie, o czym jest post przedinicjalizacją i typem inicjalizacji , to to, że jeśli nie masz statycznego konstruktora, środowisko wykonawcze może go zainicjować w dowolnym momencie (ale przed użyciem). Jeśli masz konstruktora statycznego, kod w konstruktorze statycznym może zainicjować pola, co oznacza, że środowisko wykonawcze może zainicjować pole tylko wtedy, gdy poprosisz o typ.
Jeśli więc nie chcesz, aby środowisko wykonawcze inicjowało pola „proaktywnie” przed ich użyciem, dodaj konstruktor statyczny.
Tak czy inaczej, jeśli implementujesz singletony, albo chcesz, aby inicjalizowało je możliwie jak najbardziej leniwie, a nie wtedy, gdy środowisko wykonawcze uzna, że powinno zainicjować zmienną - lub zapewne nie obchodzi cię to. Z twojego pytania przypuszczam, że chcesz je jak najszybciej.
To spotyka się z postem Jona na temat singletonów , który IMO jest głównym tematem tego pytania. Aha i wątpliwości :-)
Chciałbym zaznaczyć, że jego singleton # 3, który zaznaczył jako „zły”, jest w rzeczywistości poprawny (ponieważ blokada automatycznie implikuje barierę pamięci przy wyjściu ). Powinien także być szybszy niż singleton # 2, jeśli używasz instancji więcej niż jeden raz (co jest mniej więcej celem singletonu :-)). Tak więc, jeśli naprawdę potrzebujesz leniwej implementacji singletona, prawdopodobnie wybrałbym tę - z prostych powodów, dla których (1) jest bardzo jasne dla wszystkich, którzy czytają Twój kod, co się dzieje i (2) wiesz, co się stanie z wyjątkami.
Jeśli zastanawiasz się: nigdy nie użyłbym singletona # 6, ponieważ może to z łatwością prowadzić do impasu i nieoczekiwanego zachowania z wyjątkami. Aby uzyskać szczegółowe informacje, patrz: tryb blokowania leniwego , w szczególności ExecutionAndPublication.
źródło
Regarding question (1): The answer from Jon is correct ...
Jon Skeet ma zawsze rację ....