Abstrakte Basisklasse jeweils abgeleiteten Klassen zu zwingen Singleton zu sein
Frage
Wie kann ich eine abstrakte Klasse zu machen, dass jeder abgeleiteten Klassen zwingen soll Singleton sein? Ich verwende C #.
Lösung
Wenn Sie Kompilierung Überprüfung erzwingen möchten, ist dies nicht möglich. Mit der Laufzeit überprüft, dies zu tun. Es ist nicht schön, aber es ist möglich. Hier ein Beispiel:
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()
{
}
}
Andere Tipps
, die nicht funktionieren würde, weil die Singletons Bedürfnisse Somwhere einen statischen Zugang und das nicht gezwungen werden kann.
für singletonimplemention + Beispiele siehe: Implementierung der Singleton-Muster in C #
mit Singleton Mitteln private Konstruktoren. Aber Sie wissen, private Mitglieder können nicht vererbt werden. In C ++ gab es Vorlagen, so dass Sie einen Singleton aus einer Template-Klasse erstellen können. in C # gibt es keine Vorlagen, so dass Sie Ihre eigenen Konstrukteuren für jedes Singleton Sie haben zu schreiben.
Hier ist ein (hässlich) Weg, dies zu tun. Es könnte wahrscheinlich vereinfacht und verbessert werden, aber es ist mein erstes gehen sie an.
Die Idee ist es, zunächst die Basisklasse eine generische abstrakte Klasse zu machen (wie in den Kommentaren oben erwähnt), aber der Typ-Parameter gezwungen ist, von der Basisklasse selbst abzuleiten. Dies ermöglicht die Basisklasse der einzelne Instanz des abgeleiteten Typs zu handhaben. Beachten Sie alle abgeleiteten Klassen sollten abgedichtet werden, wie bei jedem Singletonklasse.
Als nächstes wird ein geschützter Konstruktor erlaubt, aber erforderlich, um eine Instanz einer Sonderklasse zu akzeptieren, SingletonKey, der eine modifizierte Singleton ist. Abgeleitete Klassen haben Zugriff auf die SingletonKey Klassendefinition, aber die Basisklasse behält private Kontrolle über die einzige erlaubte Instanz und damit über den Bau aller abgeleiteten Objekte.
Drittens ist die Basisklasse müssen in der Lage sein den Konstruktor der abgeleiteten Klasse zu nennen, aber das ist etwas schwierig. Der Compiler wird sich beschweren, wenn Sie versuchen, den abgeleiteten Schlüssel Konstruktor aufzurufen, da es nicht garantiert ist zu existieren. Die Lösung ist ein statisch Delegierten hinzuzufügen, dass die abgeleitete Klasse initialisiert werden muss. So dass jeder abgeleiteten Klassen müssen eine einfache Initialisierung Verfahren bereitzustellen. Diese Initialisierung Methode explizit die Instanz zum ersten Mal in Code für den Zugriff werden soll, bevor oder ein Laufzeitfehler zur Folge hat.
genanntpublic 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)
{
}
}
Klassen in Java oder C # sind nicht "First-Class". Der statische Teil einer Klasse kann nicht von den Unterklassen geerbt oder außer Kraft gesetzt werden. Sehen Sie diese Antwort für weitere Details. Plus, Sie haben kein Konzept der Meta-Klasse.
In Sprachen wie Smalltalk oder Ruby, können Sie einen neuen metaclass Singleton
definieren, die ein Verfahren getInstance
definiert. Dann können Sie ClassA
und ClassB
werden, um Instanzen des Singleton
metaclass definieren. Dann automatisch beide Klassen ein Verfahren getInstance
aussetzen, die verwendet werden können Instanzen objectA
oder objectB
zu erstellen. Ist das nicht cool? Nun, in der Praxis Sie metaclass nicht häufig verwenden, und die Singleton ist eigentlich die einzige Nutzung von ihnen, die Sinn macht und dass ich mir bewusst bin.
Ich glaube, ich versuchte, etwas ähnliches zu erreichen, das heißt eine gemeinsame Schnittstelle durchzusetzen und die Singletonmuster auf eine Gruppe von Klassen. Dies war meine Lösung:
// 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;
}
}
Hier ist ein einfacher 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();
}
}
Bitte beachten Sie, dass Sie bequem die gemeinsame Schnittstelle von den generischen abstrakten Singletonklasse entfernen können, wenn Sie nur die grundlegende „Vorlage“ benötigen.
Hier ist meine Implementierung von Singleton Vererbung:
using System;
using System.Reflection;
namespace Mik.Singleton
{
class Program
{
static void Main()
{
//You can not create an instance of class directly
//Singleton1 singleton1 = new Singleton1();
Singleton1 singleton1 = Singleton1.Instance;
Singleton2 singleton2 = Singleton2.Instance;
Console.WriteLine(singleton1.Singleton1Text);
Console.WriteLine(singleton2.Singleton2Text);
Console.ReadLine();
}
}
public class SingletonBase<T> where T : class
{
#region Singleton implementation
private static readonly object lockObj = new object();
private static T _instance;
protected SingletonBase() { }
public static T Instance
{
get
{
if (_instance == null)
{
lock (lockObj)
{
if (_instance == null)
_instance = CreateInstance();
}
}
return _instance;
}
}
private static T CreateInstance()
{
ConstructorInfo constructor = typeof(T).GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null, new Type[0],
new ParameterModifier[0]);
if (constructor == null)
throw new Exception(
$"Target type is missing private or protected no-args constructor: {typeof(T).FullName}");
try
{
T instance = constructor.Invoke(new object[0]) as T;
return instance;
}
catch (Exception e)
{
throw new Exception(
"Failed to create target: type=" + typeof(T).FullName, e);
}
}
#endregion Singleton implementation
}
public class Singleton1 : SingletonBase<Singleton1>
{
private Singleton1() { }
public string Singleton1Text { get; } = "Singleton1Text value";
}
public class Singleton2 : SingletonBase<Singleton2>
{
private Singleton2() { }
public string Singleton2Text { get; } = "Singleton2Text value";
}
}