2010-05-18 15 views

Odpowiedz

2

Jeśli nie chcesz mieć możliwości sprawdzania czasu kompilacji, nie jest to możliwe. Dzięki sprawdzaniu w czasie wykonywania możesz to zrobić. Nie jest ładna, ale jest możliwa. Oto przykład:

public abstract class Singleton 
{ 
    private static readonly object locker = new object(); 
    private static HashSet<object> registeredTypes = new HashSet<object>(); 

    protected Singleton() 
    { 
     lock (locker) 
     { 
      if (registeredTypes.Contains(this.GetType())) 
      { 
       throw new InvalidOperationException(
        "Only one instance can ever be registered."); 
      } 
      registeredTypes.Add(this.GetType()); 
     } 
    } 
} 

public class Repository : Singleton 
{ 
    public static readonly Repository Instance = new Repository(); 

    private Repository() 
    { 
    } 
} 
+1

To nie jest singleton. Z pojedynczym wzorem twoje wezwanie do 'getInstance' zawsze się powiedzie. Twoja propozycja to w istocie kontrola w czasie wykonywania, która nie zapewnia żadnej korzyści ze wzoru singleton. – ewernli

5

który nie działałby, ponieważ singleton potrzebuje gdzie indziej statycznego dostępu i nie można go zmusić.

przykłady singletonimplemention + zobacz: Implementing the Singleton Pattern in C#

+0

artykuł jest naprawdę genialny. Dochodziłem do wniosku, że to nie będzie możliwe, ale wciąż szukam pomysłów. –

+1

Dla odniesienia zaktualizowany link to: http://csharpindepth.com/Articles/General/Singleton.aspx – Seph

2

Singleton oznacza posiadanie prywatnych konstruktorów. Ale wiesz, prywatni członkowie nie mogą zostać odziedziczeni. W C++ były szablony, więc można było stworzyć singleton z klasy szablonu. w języku C# nie ma szablonów, więc musisz pisać własne konstruktory prywatne dla każdego singletona, jakiego chcesz.

1

Klasy w java lub C# nie są "pierwszej klasy". Statyczna część klasy nie może być dziedziczona ani nadpisywana przez podklasy. Aby uzyskać więcej informacji, patrz this answer. Poza tym nie masz pojęcia o meta-klasie.

W językach takich jak Smalltalk lub Ruby można zdefiniować nową metaklasa Singleton, która definiuje metodę getInstance. Następnie możesz zdefiniować ClassA i ClassB instancje metaklasy . Następnie obie klasy automatycznie ujawniają metodę getInstance, która może być użyta do utworzenia instancji objectA lub objectB. Czy to nie jest fajne? Cóż, w praktyce nie używa się często metaklasy, a singleton to w rzeczywistości jedyne użycie, które ma sens i którego jestem świadomy.

1

Oto (brzydki) sposób, aby to zrobić. Prawdopodobnie można go uprościć i ulepszyć, ale jest to moje pierwsze podejście.

Chodzi o to, aby najpierw uczynić klasę podstawową ogólną klasą abstrakcyjną (jak wspomniano w komentarzach powyżej), ale parametr typu jest ograniczony, aby wywodzić się z samej klasy bazowej. Pozwala to klasie bazowej obsłużyć instancję singleton typu pochodnego. Uwaga: wszystkie wyprowadzone klasy powinny być zapieczętowane, tak jak w przypadku każdej klasy singleton.

Następnie chroniony konstruktor jest dozwolony, ale musi zaakceptować instancję klasy specjalnej SingletonKey, która jest zmodyfikowanym singletonem. Klasy pochodne mają dostęp do definicji klasy SingletonKey, ale klasa bazowa zachowuje prywatną kontrolę nad jedyną dozwoloną instancją, a tym samym nad konstrukcją wszystkich obiektów pochodnych.

Po trzecie, klasa bazowa będzie musiała być w stanie wywołać konstruktor klasy pochodnej, ale jest to nieco trudne. Kompilator będzie narzekał, jeśli spróbujesz wywołać konstruktor z kluczem pochodnym, ponieważ nie ma gwarancji, że istnieje. Rozwiązaniem jest dodanie statycznego delegata, który musi zainicjować klasa pochodna. Zatem wszelkie klasy pochodne będą musiały dostarczyć prostą metodę inicjowania. Ta metoda inicjowania powinna zostać wywołana jawnie przed próbą uzyskania dostępu do instancji po raz pierwszy w kodzie, lub wystąpi błąd środowiska wykonawczego.

public abstract class Singleton<T> where T : Singleton<T> 
{ 
    protected Singleton(SingletonKey key) { } 
    private static SingletonKey _key; 
    private static SingletonKey Key 
    { 
     get 
     { 
      if (_key == null) SingletonKey.Initialize(); 
      return _key; 
     } 
    } 
    protected class SingletonKey 
    { 
     private SingletonKey() 
     { 
     } 
     public static void Initialize() 
     { 
      if (_key == null) 
      { 
       _key = new SingletonKey(); 
      } 
     } 
    } 

    protected static Func<SingletonKey, T> Creator; 
    private static T instance; 
    public static T Instance 
    { 
     get 
     { 
      if (instance == null) instance = Creator(Key); 
      return instance; 
     } 
    } 
} 

public class MySingleton : Singleton<MySingleton> 
{ 
    public string Name { get; set; } 
    public static void Initialize() 
    { 
     Creator = (key) => new MySingleton(key); 
    } 
    protected MySingleton(SingletonKey key) : base(key) 
    { 
    } 
} 
0

Wierzę, że próbowałem osiągnąć coś podobnego, tj. Wymusić wspólny interfejs i wzorzec singleton na grupie klas.To było moje rozwiązanie:

// Common interface of my singleton classes 
public interface IMySingletonClass 
{ 
    string ValueGetter(); 

    void ValueSetter(string value); 
} 

// Generic abstract base class 
public abstract class Singleton<T>: IMySingletonClass 
{ 
    private static readonly object instanceLock = new object(); 
    private static T instance; // Derived class instance 

    // Protected constructor accessible from derived class 
    protected Singleton() 
    { 
    } 

    // Returns the singleton instance of the derived class 
    public static T GetInstance() 
    { 
     lock (instanceLock) 
     { 
      if (instance == null) 
      { 
       instance = (T)Activator.CreateInstance(typeof(T), true); 
      } 
      return instance; 
     } 
    } 

    // IMySingletonClass interface methods 
    public abstract string ValueGetter(); 

    public abstract void ValueSetter(string value); 
} 

// Actual singleton class 
public class MySingletonClass : Singleton<MySingletonClass> 
{ 
    private string myString; 

    private MySingletonClass() 
    { 
     myString = "Initial"; 
    } 

    public override string ValueGetter() 
    { 
     return myString; 
    } 

    public override void ValueSetter(string value) 
    { 
     myString = value; 
    } 
} 

Oto prosty test:

class Program 
{ 
    static void Main(string[] args) 
    { 
     MySingletonClass r1 = MySingletonClass.GetInstance(); 
     Console.WriteLine("R1 value = {0}", r1.ValueGetter()); 
     r1.ValueSetter("Changed through R1"); 
     MySingletonClass r2 = MySingletonClass.GetInstance(); 
     Console.WriteLine("R2 value = {0}", r2.ValueGetter()); 

     Console.ReadKey(); 
    } 
} 

Należy pamiętać, że można łatwo usunąć wspólny interfejs z generycznej klasy abstrakcyjnej singleton jeśli wystarczy podstawowy „szablon” .

Powiązane problemy