什么是C#中好的线程安全单例通用模板模式

时间:2020-03-06 14:24:55  来源:igfitidea点击:

我有以下Csingleton模式,有什么改进的方法吗?

public class Singleton<T> where T : class, new()
    {

        private static object _syncobj = new object();
        private static volatile T _instance = null;
        public static T Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_syncobj)
                    {
                        if (_instance == null)
                        {
                            _instance = new T();
                        }
                    }
                }
                return _instance;
            }
        }

        public Singleton()
        { }

    }

首选用法示例:

class Foo : Singleton<Foo> 
{
}

有关的:

NET的明显单例实现?

解决方案

由Judith Bishop提供,http://patterns.cs.up.ac.za/

这种单例模式实现可确保延迟初始化。

//  Singleton PatternJudith Bishop Nov 2007
//  Generic version

public class Singleton<T> where T : class, new()
{
    Singleton() { }

    class SingletonCreator
    {
        static SingletonCreator() { }
        // Private object instantiated with private constructor
        internal static readonly T instance = new T();
    }

    public static T UniqueInstance
    {
        get { return SingletonCreator.instance; }
    }
}

public sealed class Singleton
{
   private static readonly Singleton instance = new Singleton();

   private Singleton(){}

   public static Singleton Instance
   {
      get 
      {
         return instance; 
      }
   }
}

.NET在初始化顺序方面没有歧义。但这会引起线程问题。

根据Jon Skeet在"用C实现单例模式"中的描述,我们发布的代码实际上被认为是错误代码,因为按照ECMA CLI标准进行检查时,它似乎已损坏。

还要注意:每次用新的T类型实例化对象时,它都会成为另一个实例;它不会反映在原始单例中。

我很喜欢原始答案,唯一缺少的内容(根据blowdart发布的链接)是使_instance变量易变,以确保它实际上已在锁中设置。
当我不得不使用单身人士时,我实际上使用了吹牛的解决方案,但是我不需要迟到实例等等。

我们不需要所有这些,Calready内置了一个不错的单例模式。

static class Foo

如果我们需要比这更有趣的东西,则新单例可能会与众不同,以至于通用模式将变得毫无用处。

编辑:通过"任何更有趣的"我包括继承。如果我们可以从单例继承,则不再是单例。

该代码无法编译,我们需要在T上使用"类"约束。

另外,此代码需要在目标类上使用公共构造函数,这对单例来说不好,因为我们无法在编译时仅通过Instance属性(或者字段)来获取(单个)实例。如果除Instance之外没有其他任何静态成员,则可以这样做:

class Foo
{
  public static readonly Instance = new Foo();
  private Foo() {}
  static Foo() {}
}

它是线程安全的(由CLR保证),并且是惰性的(通过对类型的首次访问创建实例)。有关BeforeFieldInit以及此处为什么需要静态构造函数的更多讨论,请参见http://www.yoda.arachsys.com/csharp/beforefieldinit.html。

如果要在类型上具有其他公共静态成员,但仅在访问实例时创建对象,则可以创建嵌套类型,例如在http://www.yoda.arachsys.com/csharp/singleton.html中

我不认为我们真的想"烧掉基类",以便可以节省两行代码。我们实际上并不需要基类来实现单例。

每当我们需要单身人士时,只需执行以下操作:

class MyConcreteClass
{
  #region Singleton Implementation

    public static readonly Instance = new MyConcreteClass();

    private MyConcreteClass(){}

  #endregion

    /// ...
}

关于在不同线程上的此答案的更多详细信息:如何在C#中实现单例?

但是,该线程不使用泛型。

我一直在寻找更好的Singleton模式,并喜欢这种模式。因此,将其移植到VB.NET可能对其他人有用:

Public MustInherit Class Singleton(Of T As {Class, New})
    Public Sub New()
    End Sub

    Private Class SingletonCreator
        Shared Sub New()
        End Sub
        Friend Shared ReadOnly Instance As New T
    End Class

    Public Shared ReadOnly Property Instance() As T
        Get
            Return SingletonCreator.Instance
        End Get
    End Property
End Class

我对确保按需创建实例数据的贡献:

/// <summary>Abstract base class for thread-safe singleton objects</summary>
/// <typeparam name="T">Instance type</typeparam>
public abstract class SingletonOnDemand<T> {
    private static object __SYNC = new object();
    private static volatile bool _IsInstanceCreated = false;
    private static T _Instance = default(T);

    /// <summary>Instance data</summary>
    public static T Instance {
        get {
            if (!_IsInstanceCreated)
                lock (__SYNC)
                    if (!_IsInstanceCreated)
                        _Instance = Activator.CreateInstance<T>();
            return _Instance;
        }
    }
}

pff ...再次... :)
我对确保按需创建实例数据的贡献:

/// <summary>Abstract base class for thread-safe singleton objects</summary>
/// <typeparam name="T">Instance type</typeparam>
public abstract class SingletonOnDemand<T> {
  private static object __SYNC = new object();
  private static volatile bool _IsInstanceCreated = false;
  private static T _Instance = default(T);

  /// <summary>Instance data</summary>
  public static T Instance {
    get {
      if (!_IsInstanceCreated)
        lock (__SYNC)
          if (!_IsInstanceCreated) {
            _Instance = Activator.CreateInstance<T>();
            _IsInstanceCreated = true;
          }
          return _Instance;
    }
  }
}

根据要求,从我的原始答案中交叉发布另一个问题。

我的版本使用Reflection,在派生类中与非公共构造函数一起使用,具有线程安全性(显然),具有惰性实例化(根据我在下面找到的链接文章):

public class SingletonBase<T> where T : class
{
    static SingletonBase()
    {
    }

    public static readonly T Instance = 
        typeof(T).InvokeMember(typeof(T).Name, 
                                BindingFlags.CreateInstance | 
                                BindingFlags.Instance |
                                BindingFlags.Public |
                                BindingFlags.NonPublic, 
                                null, null, null) as T;
}

几年前我拿起了它,不知道有多少,但是如果不是我,那么在代码上进行搜索可能会找到该技术的原始来源。

这是我可以找到的最古老的代码来源,而不是我发布的代码。

:/朱迪思·毕晓普(Judith Bishop)的通用"单人"模式似乎有点缺陷,它总是可以创建T类型的多个实例,因为构造函数必须在此"模式"中公开使用。在我看来,它与单例绝对无关,它只是一种工厂,总是返回相同的对象,但不会使其成为单例……只要一个类的实例可以超过一个不能是单身人士。有什么理由使这种模式获得最高评价?

public sealed class Singleton
{
  private static readonly Singleton _instance = new Singleton();

  private Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      return _instance;
    }
  }
}

静态初始值设定项被认为是线程安全的。任何意义

public static class LazyGlobal<T> where T : new()
{
    public static T Instance
    {
        get { return TType.Instance; }
    }

    private static class TType
    {
        public static readonly T Instance = new T();
    }
}

// user code:
{
    LazyGlobal<Foo>.Instance.Bar();
}

或者:

public delegate T Func<T>();

public static class CustomGlobalActivator<T>
{
    public static Func<T> CreateInstance { get; set; }
}

public static class LazyGlobal<T>
{
    public static T Instance
    {
        get { return TType.Instance; }
    }

    private static class TType
    {
        public static readonly T Instance = CustomGlobalActivator<T>.CreateInstance();
    }
}

{
    // setup code:
    // CustomGlobalActivator<Foo>.CreateInstance = () => new Foo(instanceOf_SL_or_IoC.DoSomeMagicReturning<FooDependencies>());
    CustomGlobalActivator<Foo>.CreateInstance = () => instanceOf_SL_or_IoC.PleaseResolve<Foo>();
    // ...
    // user code:
    LazyGlobal<Foo>.Instance.Bar();
}

前一阵子看到它使用反射来访问私有(或者公共)默认构造函数:

public static class Singleton<T>
{
    private static object lockVar = new object();
    private static bool made;
    private static T _singleton = default(T);

    /// <summary>
    /// Get The Singleton
    /// </summary>
    public static T Get
    {
        get
        {
            if (!made)
            {
                lock (lockVar)
                {
                    if (!made)
                    {
                        ConstructorInfo cInfo = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null);
                        if (cInfo != null)
                            _singleton = (T)cInfo.Invoke(new object[0]);
                        else
                            throw new ArgumentException("Type Does Not Have A Default Constructor.");
                        made = true;
                    }
                }
            }

            return _singleton;
        }
    }
}

尝试以线程安全和懒惰的方式(thx到wcell)实现此Singleton设计模式的通用Singleton类。

public abstract class Singleton<T> where T : class
{
    /// <summary>
    /// Returns the singleton instance.
    /// </summary>
    public static T Instance
    {
        get
        {
            return SingletonAllocator.instance;
        }
    }

    internal static class SingletonAllocator
    {
        internal static T instance;

        static SingletonAllocator()
        {
            CreateInstance(typeof(T));
        }

        public static T CreateInstance(Type type)
        {
            ConstructorInfo[] ctorsPublic = type.GetConstructors(
                BindingFlags.Instance | BindingFlags.Public);

            if (ctorsPublic.Length > 0)
                throw new Exception(
                    type.FullName + " has one or more public constructors so the property cannot be enforced.");

            ConstructorInfo ctorNonPublic = type.GetConstructor(
                BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[0], new ParameterModifier[0]);

            if (ctorNonPublic == null)
            {
                throw new Exception(
                    type.FullName + " doesn't have a private/protected constructor so the property cannot be enforced.");
            }

            try
            {
                return instance = (T)ctorNonPublic.Invoke(new object[0]);
            }
            catch (Exception e)
            {
                throw new Exception(
                    "The Singleton couldnt be constructed, check if " + type.FullName + " has a default constructor", e);
            }
        }
    }
}