如何抽象出一个单子类?[英] How to abstract a singleton class?

本文是小编为大家收集整理的关于如何抽象出一个单子类?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

这就是我写单身课的方式.

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.
  • 它必须具有本地阅读属性才能获取实例.

问题

我在多个类上使用了此模式,始终必须编写相同的代码.我该如何编写每当需要单身人士时重复使用的东西?

推荐答案

您可以使用自我引用通用类型约束和a" a" new()"类型约束.

"

"新"约束确保任何子类都将始终具有无参数构造函数,因此_instance = new T();将始终可用.

自我引用类型约束确保"实例"静态属性始终返回正确的类型;不是"基础"类型.您的Singleton基课看起来像这样:

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

当然,如果您希望单身人士成为通用,还应稍微更改"创建单身实例"代码,以使用" 双检查锁"模式,或懒惰 class,使其成为线程安全.

大警告:如果您使用此方法,则" new()"约束几乎可以确保您的课程将始终具有公开的无参数构造函数.这意味着您的最终用户总是只能致电new MyChildSingleton(),如果他们真的想要,完全绕过您的Singleton实例.您的单身人士将是"按照惯例",而不是严格执行.要解决这个问题将需要更多的工程.在上面的情况下,约定似乎应该将静态实例" Default"而不是" Instance"命名.这巧妙地传达了您的班级提供"建议"单例实例的事实,但在技术上是可选的.

我已经尝试了严格执行单例模式的尝试,最终结果是使用反射来手动调用私人构造函数.您可以看到我的完整代码尝试在这里p>

其他推荐答案

真正的解决方案是从BtownTKD的方法开始,但使用Activator.createinstance方法来增强它,该方法允许您的孩子课程保留私人构造函数.

父级

public abstract class SingletonBase<T> where T : SingletonBase<T>
{
    private static readonly Lazy<T> Lazy =
        new Lazy<T>(() => Activator.CreateInstance(typeof(T), true) as T);

    public static T Instance => Lazy.Value;
}

儿童班

public sealed class MySingleton : SingletonBase<MySingleton>
{
    private MySingleton() { }
}

在此处进行完整实施示例.

其他推荐答案

添加到BtownTKD的答案中,在运行时限制构造函数的调用实际上非常简单(不确定在汇编中可以使用).您所要做的就是在单键基键中添加一个受保护的构造函数,如果_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;
        }
    }
}

我必须说我没有进行密集的多线程测试,但是正如某些人已经说过的,您可以随时使用旧的双重检查技巧.

本文地址:https://www.itbaoku.cn/post/627791.html

问题描述

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 private or 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.

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?

推荐答案

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

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

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 Lazy class, 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.

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.

其他推荐答案

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.

Parent Class

public abstract class SingletonBase<T> where T : SingletonBase<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 MySingleton : SingletonBase<MySingleton>
{
    private MySingleton() { }
}

Full Implementation Example Here

其他推荐答案

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.

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

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.