Rekomendowana implementacja wzorca Singleton w C# wygląda tak (plus oczywiście prywatny konstruktor):
1: private static object _lock = new object(); 2: private static Singleton _instance; 3: public static Singleton Instance 4: { 5: get 6: { 7: if (_instance == null) 8: { 9: lock (_lock) 10: { 11: if (_instance == null) 12: { 13: _instance = new Singleton(); 14: } 15: } 16: } 17: return _instance; 18: } 19: }
Czy jednak nie można zawrzeć całego tego “szumu” w jednej linijce kodu?
1: public static readonly Singleton Instance = new Singleton();
Cechy współdzielone z implementacją pierwszą:
– spełnione założenia Singletona: jedna, dostępna globalnie instancja
– bezpieczna wielowątkowość
Zalety:
– jedna banalnie prosta linijka kodu
Wady:
– możliwa inicjalizacja przed pierwszym faktycznym wykorzystaniem instancji
Z tej implementacji korzystam od dawna i… czy coś mi umknęło? Teoretyczne odłożenie procesu tworzenia obiektu kosztuje nas 18 linii kodu? W ogromnie przeważającej większości przypadków czytelność i mniejsza ilość kodu są z pewnością ważniejsze. A może jednak nie zauważam jakiejś oczywistości?
Nie przegap kolejnych postów!
Dołącz do ponad 9000 programistów w devstyle newsletter!
@Procent mhhh, ze strony MS: http://msdn.microsoft.com/en-us/library/ms998558.aspx proponuja ta samo co ty tylko za pomoca property (przyklad srodkowy). Co do Twojego rozwiazania, jest zgrabne i ladne :) ale jak to sie zachowa w srodowisku wielowatkowym? Tutaj jest o tym cala dyskusjka – problemie z lockiem itp: http://blogs.msdn.com/brada/archive/2004/05/12/130935.aspx – plus linki do art na ten temat w .NET 2.0 i 3.5 zas tutaj trafiam dosc czesto nie zaleznie czego szukam ;) http://www.yoda.arachsys.com/csharp/singleton.html Gutek PS: Ja osobiscie sobie swojego czasu stworzylem szablony do klas w VS i teraz jak chce Singleton to klikam create i mam :) Wiec… myslac o zaoszczedzaniu czasu na… Read more »
Problemy są jak pojawi się wyjątek w konstruktorze obiektu Singletonu. Wtedy cała klasa jest kompletnie bezużyteczna. Przykład:
class Program
{
static void Main(string[] args)
{
var name = MySingleton.Name;
}
}
public class MySingleton
{
public static readonly string Name = “aaa”;
public static readonly MySingleton Instance = new MySingleton();
private MySingleton()
{
throw new Exception();
}
}
Powyższy kod wywala TypeInitializationException. Ponadto, wersja 18 linijkowa daje Ci ponowną szansę na utworzenie obiektu w razie gdy za pierwszym raz wyskoczy exception.
Gutek: mozesz podac dokladniej o co chodzi z tym problemem wielowatkowosci w przypadku “jednolinijkowca”? Gutek: to “evil costam” to tak troche nie na temat. w KAZDYM wzorcu/szablonie programowania mozna przegiac (http://discuss.joelonsoftware.com/default.asp?joel.3.219431.12), wiec taki disclaimer mozna dowac gdzie sie chce i to nic nie wnosi – nawet jestem w stanie chyba podac takiego “evila” dla dowolnego najwspanialeszego przykladu wzorca/solution jakie zaproponujesz. Poprostu nie mozna programowac na “małpke”, tylko trzeba myslec. jakubin: hmm.. przyklad nietrafiony z ta “ponowna szansa na utowrzenie obiektu”. Zakladamy ze mamy ten sam typ/klase (rozna implementacja “wnetrznosci” powoduje nieporownywalnosc rozwiazan, bo… sa inne) ale o roznej implementacji “singletona”.… Read more »
@Wojciech: Może zdarzyć się, iż utworzenie obiektu danego singletona nie jest możliwe w każdym momencie (bo np. wymaga informacji konfiguracyjnych, które jeszcze nie zostały załadowane). Oczywiście, trudno sobie wyobrazić taką sytuację w dobrze zaprojektowanym systemie.
Jednakże dyskutujemy tutaj o wzorcu singletona jako takim, więc analizując za i przeciw trzeba wziąć pod uwagę każdą, nawet najgłupszą implementację konstruktora:
if (new Random().Next(2) == 0)
throw new Exception(“Bad luck”);
Dlatego uważam, że w tym kontekście nie ma przykładów trafionych i nietrafionych.
@Wojtek Ad1) Zadalem pytanie, bo nie znalem odpowiedzi (% moglbys odpowiedziec co?;) ), ale skoro chcesz abym wytlumaczyl to prosze. Normalnie jak dostajesz sie poprzez property do instancji to statujesz tak zwany lazy initialization. Powoduje to ze moze sie zdazyc przypdek ze w jednym moemencie zostana utworzone dwie instancje klasy, co jednoznacznie mowi ze to juz nie jest singleton. Dlatego tez przewaznie stosowany byl lock z tak zwanym double checkiem (o czym masz dosc sporo w linkach ktore juz wyzej podalem wiec nie bede wchodzil wszczegoly). Jednakze jak teraz przeczytalem, .NET frameowork zapewnia thread safty na statycznych inicjalizatorach, wiec przyklad… Read more »
Gutek: dzieki za naprodukowanie sie. chodzili mi o potwierdzenie ze jednak jednolinijkowiec bedzie thread safe (lub null czasami, ale to nie ma znaczenie bo mowimy o jednoinstancyjnosci)“
A co do lazy initialization jednolinijkowca to ja bym sie tak nie rozpedzal z tymi twierdzeniami, tylko uwarunkowal “otoczeniem” jednolinijkowca.
Nie rozumieme tego calego szumu ze to nie lazy itp… przeca napisal o tym w wadach nie? – oczywiscie nikt nie zabrania rozpisywania sie w stylu: “potwierdzam dziala
Przykład 1 linijkowy jest jak najbardziej poprawnym singletonem. Na 100 Procentów:)
Przykład 18-to linijkowy nie działa. Po pierwsze nie działa dlatego, że zmienna _instance nie jest “volatile”. Dlatego nie działa na wielu wątkach. Po drugie nie działa na maszynach IA64, bo tam model pamięci jest jeszcze luźniejszy i, o ile dobrze pamiętam, wymaga memory barrier.
Ja osobiście uważam, że odkąd autorzy kontenerów DI podjęli trud zaimplementowania trybu singletonowego za nas, nie ma sensu powtarzać ich wysiłku;)