题
我如何做一个抽象类,不得强迫每个派生类是单身?我用C#。
解决方案
当您要执行编译时检查,这是不可能的。随着运行时检查,你可以做到这一点。它不漂亮,但它是可能的。下面是一个例子:
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()
{
}
}
其他提示
这是行不通的,因为单需求somwhere静态访问并能不强求。
有singletonimplemention +实例参见:实施Singleton模式在C#
具有私有构造函数的Singleton装置。但你知道私有成员不能被继承。在C ++中有模板,这样你就可以从模板创建类单身。在C#中,没有模板,所以你必须编写自己的私有构造你希望每个单。
下面是一个(丑陋)的方式来做到这一点。这也许可以简化和改进,但它是我在它第一次去。
我们的想法是首先使基类(如上面提到的评论)的通用抽象类,但类型参数被约束从基类本身导出。这允许基类来处理派生类型的单个实例。注意所有派生类应密封,与任何单独的类。
接着,受保护的构造是允许的,但必须接受特殊的类,SingletonKey,其是经修饰的单例的实例。派生类可以访问SingletonKey类的定义,但基类保留对只允许实例的专用控制,因此在施工所有派生对象。
第三,基类将需要能够调用派生类的构造,但是这是略微棘手。编译器会抱怨,如果你尝试调用派生密钥的构造,因为它不能保证存在。解决的办法是添加一个静态委托派生类必须初始化。所以,任何派生类将需要提供一个简单的初始化方法。该初始化方法应被调用明确地试图访问实例首次在代码之前,或将导致运行时错误。
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)
{
}
}
在Java或C#类不是“第一级”。一类的静态部分不能继承或子类覆盖。请参见这个答案了解更多详情。另外,你不必元类的概念。
在如Smalltalk或Ruby的语言,则可以定义一个新的元类Singleton
它定义了一个方法getInstance
。然后,你可以定义ClassA
和ClassB
是Singleton
元类的实例。然后这两个类自动露出,其可用于创建实例getInstance
或objectA
的方法objectB
。是不是很酷?那么,在实践中不使用元类频繁,且单实际上是他们的唯一用途是有意义,我所知道的。
我相信我试图实现类似即强制执行共同的界面和上一组类的单例模式的东西。这是我的解决方案:
// 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;
}
}
下面是一个简单的测试:
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();
}
}
请注意,你可以很容易地从一般的抽象单身类去除常见的接口,如果你只需要基本的“模板”。
在这里我实现辛格尔顿继承:
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";
}
}