C# 如何抽象一个单例类?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16745629/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 01:49:41  来源:igfitidea点击:

How to abstract a singleton class?

c#design-patternssingleton

提问by Reactgular

This is how I write my singleton classes.

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

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

How To Create A Singleton Pattern That Is Reusable?

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

Singleton patterns present the following challenges.

单例模式存在以下挑战。

  • The constructor is privateor protected.
  • A base class can't instantiate an inherited class. So you can reuse a common abstract MyAbstractSingletonClass.
  • It has to have a local read-only property to get the instance.
  • 构造函数是privateor protected
  • 基类不能实例化继承的类。所以你可以重用一个通用的抽象MyAbstractSingletonClass
  • 它必须具有本地只读属性才能获取实例。

The Problem

问题

I'm using this pattern on a number of classes and always have to write the same code. How can I write something that is reused whenever I need a singleton?

我在许多类上使用这种模式,并且总是必须编写相同的代码。当我需要单例时,如何编写可以重用的东西?

采纳答案by BTownTKD

You can achieve this using a combination of a self-referencing generic type constraint, and a "new()" type constraint.

您可以使用自引用泛型类型约束和“ new()”类型约束的组合来实现这一点。

The "new" constraint ensures that any child class will always have a parameterless constructor, so _instance = new T();will always work.

“新”约束确保任何子类将始终具有无参数构造函数,因此_instance = new T();将始终有效。

The self-referencing type constraint ensures that the "Instance" static property always returns the correct Type; not the "base" type. Your singleton base class would look something like this:

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

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

Your child classes will look like this:

您的子类将如下所示:

public class MyChildSingleton : SingletonBase<MyChildSingleton>
{
    //Done!
}

Of course, if you want your singleton to be general-purpose, you should also change your "create singleton instance" code slightly, to use the "double-check lock" pattern, or the Lazyclass, to make it thread-safe.

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

The big caveat: if you use this method, the "new()" constraint pretty much ensures that your class will always have a public, parameterless constructor. That means your end-users could always just call new MyChildSingleton()if they really wanted, bypassing your singleton instance entirely. Your singleton would be "by convention," instead of strictly enforced. To get around this would take a bit more engineering. In the above scenario, the convention seems to be that you should name your static instance "Default" instead of "Instance." This subtly conveys the fact that your class offers a 'suggested' singleton instance, but using it is technically optional.

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

I've made some attempts to strictly enforce the singleton pattern, and the end result was to use reflection to manually invoke a private constructor. You can see my full code attempt here.

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

回答by Ilya Kogan

The simple answer is that you can't implement the singleton pattern in a base class.

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

However, you can implement other creational design patterns that may be suitable for what you're trying to accomplish. For example, take a look at Abstract Factory.

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

回答by slugster

You are correct - as it currently stands you cannot achieve this. But you can approach it using generics, note that with this approach you will get one singleton instance for each unique derived type:

您是对的 - 按照目前的情况,您无法实现这一目标。但是您可以使用泛型来处理它,请注意,使用这种方法,您将为每个唯一的派生类型获得一个单例实例

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

}

回答by dan

I recently suggested this answer to a related question:

我最近对一个相关问题提出了这个答案:

https://stackoverflow.com/a/20599467

https://stackoverflow.com/a/20599467

With this method, the base class manages creation of all instances of derived classes, as all derived class constructors require an object that only the base class can provide, and there is no need for the parameterless constructor restriction.

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

回答by Eugene Marin

Adding to BTownTKD's answer, it's actually pretty simple to restrict a constructor call in runtime (not sure possible in compilation). All you do is add a protected constructor in SingletonBase, that throws an exception if _instance is not null. The exception will be thrown even if the constructor is the first thing to be called from outside.

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

I managed to apply this technic in a singleton base, and also make it lazy and thread safe as described here: http://csharpindepth.com/Articles/General/Singleton.aspx

我设法在单例基础中应用了这种技术,并使其变得懒惰和线程安全,如下所述:http: //csharpindepth.com/Articles/General/Singleton.aspx

The result (with usage explanation):

结果(附用法说明):

/// <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;
        }
    }
}

I must say I haven't done intensive multi-threading testing, but as some already said, you can always use the old double-check trick.

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

回答by 2Toad

A generic, streamlined, implementation of the thread-safe lockless pattern:

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

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

The static constructor is excluded in favor of performance, since laziness is not required, and the property is replaced with a public field for brevity

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

Implementation

执行

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

Use

Foo.Instance.Bar();

回答by Buvy

The true solution is starting with BTownTKD's approach but augmenting it with the Activator.CreateInstance method which allows your child classes to keep the private constructors.

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

Parent Class

家长班

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 => Lazy.Value;
}

Child Class

儿童班

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

Full Implementation Example Here

完整的实现示例在这里

回答by Hasan Fathi

My suggested example:

我建议的例子:

base class

基类

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;
  }
}

Usage

用法

public class yourClass : SingletonBase<yourClass>
{
   public yourMethod()
   {
   }
}

use your singleton class like this:

像这样使用你的单例类:

yourClass.Instance.yourMethod();

for more info see my answer source in this link

有关更多信息,请参阅此链接中的我的答案来源

回答by jalley

I believe with @Buvy's solution, you will get an instance per thread which may be what he intended. You will have to modify it a bit to get a single instance across threads.

我相信使用@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; } }

}