这就是我编写单例类的方法。
public class MyClass
{
/// <summary>
/// Singleton
/// </summary>
private static MyClass instance;
/// <summary>
/// Singleton access.
/// </summary>
public static MyClass Instance
{
get
{
if (_instance == null)
{
_instance = new MyClass();
}
return _instance;
}
}
private MyClass() { .... }
}
单身模式带来了以下挑战。
private
或protected
。MyAbstractSingletonClass
。我在许多类上使用这种模式,并且总是必须编写相同的代码。我怎么能写出一些在我需要单身时重复使用的东西?
答案 0 :(得分:50)
您可以使用self-referencing generic type constraint和“new()”类型约束的组合来实现此目的。
“new”约束确保任何子类始终具有无参数构造函数,因此_instance = new T();
将始终有效。
自引用类型约束确保“Instance”静态属性始终返回正确的Type;不是“基础”类型。你的单例基类看起来像这样:
public abstract class SingletonBase<T>
where T : SingletonBase<T>, new()
{
private static T _instance = new T();
public static T Instance
{
get
{
return _instance;
}
}
}
您的孩子课程将如下所示:
public class MyChildSingleton : SingletonBase<MyChildSingleton>
{
//Done!
}
当然,如果您希望您的单例是通用的,您还应该稍微更改“创建单例实例”代码,使用“double-check lock”模式或Lazy类,使其成为线程安全的。
最大的警告:如果使用此方法,“new()”约束几乎可以确保您的类始终具有公共的无参数构造函数。这意味着您的最终用户可以随时调用new MyChildSingleton()
,如果他们真的想要,完全绕过您的单例实例。你的单身人士将“按惯例”,而不是严格执行。为了解决这个问题需要更多的工程。在上面的场景中,约定似乎是您应该将静态实例命名为“Default
”而不是“Instance
”。这巧妙地传达了这样一个事实,即你的类提供了一个“建议的”单例实例,但使用它在技术上是可选的。
我已经尝试严格执行单例模式,最终结果是使用反射来手动调用私有构造函数。您可以看到我的完整代码尝试here。
答案 1 :(得分:5)
添加到BTownTKD的答案,在运行时限制构造函数调用实际上非常简单(在编译时不确定)。您所做的就是在SingletonBase中添加一个受保护的构造函数,如果_instance不为null,则抛出异常。即使构造函数是从外部调用的第一个东西,也会抛出异常。
我设法在单例库中应用此技术,并使其变得懒惰和线程安全,如下所述:http://csharpindepth.com/Articles/General/Singleton.aspx
结果(使用说明):
/// <summary>
/// Generic singleton class, providing the Instance property, and preventing manual construction.
/// Designed as a base for inheritance trees of lazy, thread-safe, singleton classes.
/// Usage:
/// 1. Sub-class must use itself, or its sub-class, as the type parameter S.
/// 2. Sub-class must have a public default constructor (or no constructors).
/// 3. Sub-class might be abstract, which requires it to be generic and demand the generic type
/// have a default constructor. Its sub-classes must answer all these requirements as well.
/// 4. The instance is accessed by the Instance getter. Using a constructor causes an exception.
/// 5. Accessing the Instance property in an inner initialization in a sub-class constructor
/// might cause an exception is some environments.
/// </summary>
/// <typeparam name="S">Lowest sub-class type.</typeparam>
public abstract class Singleton<S> where S : Singleton<S>, new()
{
private static bool IsInstanceCreated = false;
private static readonly Lazy<S> LazyInstance = new Lazy<S>(() =>
{
S instance = new S();
IsInstanceCreated = true;
return instance;
});
protected Singleton()
{
if (IsInstanceCreated)
{
throw new InvalidOperationException("Constructing a " + typeof(S).Name +
" manually is not allowed, use the Instance property.");
}
}
public static S Instance
{
get
{
return LazyInstance.Value;
}
}
}
我必须说我没有进行密集的多线程测试,但正如一些人已经说过的那样,你总是可以使用旧的双重检查技巧。
答案 2 :(得分:5)
真正的解决方案是从BTownTKD的方法开始,但使用Activator.CreateInstance方法扩充它,允许您的子类保留私有构造函数。
父类
public abstract class BaseSingleton<T> where T :
BaseSingleton<T>
{
private static readonly ThreadLocal<T> Lazy =
new ThreadLocal<T>(() =>
Activator.CreateInstance(typeof(T), true) as T);
public static T Instance => Lazy.Value;
}
儿童班
public sealed class MyChildSingleton : BaseSingleton<MyChildSingleton>
{
private MyChildSingleton() { }
}
答案 3 :(得分:4)
你是对的 - 因为目前的情况你无法做到这一点。但是您可以使用泛型来处理它,请注意,使用此方法,您将获得每个唯一派生类型的单个实例:
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var x = new MyDerivedClass();
Console.WriteLine(x.ToString());
Console.WriteLine(x.Instance.ToString());
Console.ReadKey();
}
}
public abstract class MyBaseClass<T> where T : class, new()
{
protected T GetInstance()
{
if (_instance == null)
{
lock (_lockObj)
{
if (_instance == null)
_instance = new T();
}
}
return _instance;
}
public T Instance
{
get { return GetInstance(); }
}
private volatile static T _instance;
private object _lockObj = new object();
}
public class MyDerivedClass : MyBaseClass<MyDerivedClass>
{
public MyDerivedClass() { }
}
}
答案 4 :(得分:2)
简单的答案是你不能在基类中实现单例模式。
但是,您可以实现其他可能适合您要完成的创建设计模式。例如,请查看Abstract Factory。
答案 5 :(得分:1)
线程安全的lockless pattern:
的通用,简化的实现public abstract class Singleton<T> where T : class, new() {
public static readonly T Instance = new T();
}
静态构造函数被排除在外,有利于性能,因为不需要惰性,为了简洁,该属性被公共字段替换
<强>实施强>
public sealed class Foo : Singleton<Foo> {
public void Bar() {
//...
}
}
使用强>
Foo.Instance.Bar();
答案 6 :(得分:0)
我最近提出了一个相关问题的答案:
https://stackoverflow.com/a/20599467
使用此方法,基类管理所有派生类实例的创建,因为所有派生类构造函数都需要只有基类可以提供的对象,并且不需要无参数构造函数限制。
答案 7 :(得分:0)
我的建议示例:
基类
public abstract class SingletonBase<T> where T : class
{
private static readonly Lazy<T> sInstance = new Lazy<T>(() => CreateInstanceOfT());
public static T Instance { get { return sInstance.Value; } }
private static T CreateInstanceOfT()
{
return Activator.CreateInstance(typeof(T), true) as T;
}
}
用法的
public class yourClass : SingletonBase<yourClass>
{
public yourMethod()
{
}
}
像这样使用你的单身人士课程:
yourClass.Instance.yourMethod();
有关详细信息,请参阅this link
中的答案来源答案 8 :(得分:0)
我相信使用@Buvy的解决方案,您将在每个线程中获得一个实例,这可能是他想要的。您将需要对其进行一些修改,以在线程之间获得单个实例。
public abstract class BaseSingleton<T>
where T : BaseSingleton<T>
{
private static readonly Lazy<T> lazy =
new Lazy<T>(() => Activator.CreateInstance(typeof(T), true) as T);
public static T Instance { get { return lazy.Value; } }
}