如何抽象单例类?

这就是我编写单例类的方法。

public class MyClass { ///  /// Singleton ///  private static MyClass instance; ///  /// Singleton access. ///  public static MyClass Instance { get { if (_instance == null) { _instance = new MyClass(); } return _instance; } } private MyClass() { .... } } 

如何创建可重用的单例模式?

单身模式存在以下挑战。

  • 构造函数是privateprotected
  • 基类无法实例化inheritance的类。 因此,您可以重用一个常见的抽象MyAbstractSingletonClass
  • 它必须具有本地只读属性才能获取实例。

问题

我在许多类上使用这种模式,并且总是必须编写相同的代码。 我怎么能写出一些我需要单身时可以重复使用的东西?

您可以使用自引用generics类型约束和“ new() ”类型约束的组合来实现此目的。

“new”约束确保任何子类始终具有无参数构造函数,因此_instance = new T(); 永远都会有用。

自引用类型约束确保“实例”静态属性始终返回正确的类型; 不是“基础”类型。 你的单例基类看起来像这样:

 public abstract class SingletonBase where T : SingletonBase, new() { private static T _instance = new T(); public static T Instance { get { return _instance; } } } 

您的子课程将如下所示:

 public class MyChildSingleton : SingletonBase { //Done! } 

当然,如果你希望你的单例是通用的,你也应该稍微更改你的“创建单例实例”代码,使用“ 双重检查锁 ”模式或Lazy类,使其成为线程安全的。

最重要的警告 :如果使用此方法,“new()”约束几乎可以确保您的类始终具有公共的无参数构造函数。 这意味着你的最终用户可以随时调用new MyChildSingleton()如果他们真的想要,完全绕过你的单例实例。 你的单身人士将“按惯例”,而不是严格执行。 要解决这个问题需要更多的工程设计。 在上面的场景中,约定似乎是您应该将静态实例命名为“ Default ”而不是“ Instance 。 这巧妙地表达了您的类提供“建议的”单例实例的事实,但使用它在技术上是可选的。

我已经尝试严格执行单例模式,最终结果是使用reflection来手动调用私有构造函数。 您可以在此处查看我的完整代码尝试。

添加到BTownTKD的答案,在运行时限制构造函数调用实际上非常简单(在编译时不确定)。 您所做的就是在SingletonBase中添加一个受保护的构造函数,如果_instance不为null,则抛出exception。 即使构造函数是从外部调用的第一个东西,也会抛出exception。

我设法在单独的基础上应用这个技术,并使它变得懒惰和线程安全,如下所述: http : //csharpindepth.com/Articles/General/Singleton.aspx

结果(使用说明):

 ///  /// 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. ///  /// Lowest sub-class type. public abstract class Singleton where S : Singleton, new() { private static bool IsInstanceCreated = false; private static readonly Lazy LazyInstance = new Lazy(() => { 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; } } } 

我必须说我没有进行密集的multithreading测试,但正如一些人已经说过的那样,你总是可以使用旧的双重检查技巧。

真正的解决方案是从BTownTKD的方法开始,但使用Activator.CreateInstance方法扩充它,允许您的子类保留私有构造函数。

家长class

 public abstract class BaseSingleton where T : BaseSingleton { private static readonly ThreadLocal Lazy = new ThreadLocal(() => Activator.CreateInstance(typeof(T), true) as T); public static T Instance => Lazy.Value; } 

儿童class

 public sealed class MyChildSingleton : BaseSingleton { private MyChildSingleton() { } } 

完整的实现示例

你是对的 – 因为它目前的表现你无法做到这一点。 但是您可以使用generics来处理它,请注意,使用此方法,您将为每个唯一派生类型获取一个单例实例

 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 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 { public MyDerivedClass() { } } } 

简单的答案是您无法在基类中实现单例模式。

但是,您可以实现其他可能适合您要完成的创建设计模式。 例如,看一下Abstract Factory 。

线程安全无锁模式的通用,简化的实现:

 public abstract class Singleton where T : class, new() { public static readonly T Instance = new T(); } 

静态构造函数被排除在外,有利于性能,因为不需要惰性,为了简洁起见,属性被公共字段替换

履行

 public sealed class Foo : Singleton { public void Bar() { //... } } 

使用

 Foo.Instance.Bar(); 

我最近建议这个相关问题的答案:

https://stackoverflow.com/a/20599467

使用此方法,基类管理所有派生类实例的创建,因为所有派生类构造函数都需要只有基类可以提供的对象,并且不需要无参数构造函数限制。

我建议的例子:

基类

 public abstract class SingletonBase where T : class { private static readonly Lazy sInstance = new Lazy(() => 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 { public yourMethod() { } } 

像这样使用你的单例类:

 yourClass.Instance.yourMethod(); 

有关详细信息,请参阅此链接中的答案来源