31

我有以下 C# 单例模式,有什么方法可以改进它吗?

    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 的明显单例实现?

4

22 回答 22

18

根据 Jon Skeet 在Implementation the Singleton Pattern in C#中的说法,您发布的代码实际上被认为是错误代码,因为在根据 ECMA CLI 标准检查时它似乎已损坏。

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

于 2008-09-19T06:52:39.380 回答
10

此代码无法编译,您需要对 T 进行“类”约束。

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

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

它是线程安全的(由 CLR 保证)和惰性(实例是在第一次访问类型时创建的)。有关 BeforeFieldInit 的更多讨论以及为什么我们需要静态构造函数,请参阅https://csharpindepth.com/articles/BeforeFieldInit

如果您想在类型上有其他公共静态成员,但仅在访问实例时创建对象,您可以创建嵌套类型,如https://csharpindepth.com/articles/Singleton

于 2008-09-19T07:05:24.373 回答
9

由 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; }
    }
}
于 2008-09-19T06:45:48.960 回答
6

这是我使用 .NET 4 的观点

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

        private static readonly Lazy<T> instance = new Lazy<T>(()=> new T());

        public static T Instance { get { return instance.Value; } } 
    }
于 2011-10-25T15:41:55.927 回答
3

我不认为你真的想“烧掉你的基类”,这样你就可以节省 2 行代码。你真的不需要基类来实现单例。

每当您需要单例时,只需执行以下操作:

class MyConcreteClass
{
  #region Singleton Implementation

    public static readonly Instance = new MyConcreteClass();

    private MyConcreteClass(){}

  #endregion

    /// ...
}
于 2008-11-15T05:52:39.020 回答
3

有关此答案的更多详细信息,请参见另一个线程:如何在 C# 中实现单例?

但是该线程不使用generic

于 2008-12-11T15:01:51.600 回答
2
public sealed class Singleton
{
   private static readonly Singleton instance = new Singleton();

   private Singleton(){}

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

.NET 中的初始化顺序没有歧义;但这会引发线程问题。

于 2008-09-19T06:46:55.463 回答
2

:/ Judith Bishop 的通用“单例”模式似乎有点缺陷,它总是可以创建多个类型 T 的实例,因为构造函数必须是公共的才能在这个“模式”中使用它。在我看来,它与单例完全无关,它只是一种工厂,它总是返回相同的对象,但不会使其成为单例......只要可以有多个类的实例它不能是单身人士。这种模式获得最高评价的任何原因?

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

  private Singleton()
  {
  }

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

静态初始化器被认为是线程安全的。我不知道,但你根本不应该使用单例的习惯用法,如果你将我的代码包装在它不超过 3 行的上面......并且从单例继承不会使任何意义。

于 2009-08-24T16:15:24.540 回答
1

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

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

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

几年前我捡到了这个,不确定我的多少钱,但如果不是我,谷歌搜索代码可能会找到该技术的原始来源。

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

于 2009-06-18T03:39:15.760 回答
1

Microsoft在此处提供的 Double-Check Locking [Lea99] 习语与您提供的代码惊人地相似,不幸的是,这不符合 ECMA CLI 标准,即线程安全代码的清教徒视图,并且可能无法在所有情况下正常工作。

在多线程程序中,不同的线程可以尝试同时实例化一个类。出于这个原因,依赖 if 语句来检查实例是否为 null 的 Singleton 实现将不是线程安全的。不要写这样的代码!

创建线程安全单例的一种简单但有效的方法是使用嵌套类来实例化它。以下是惰性实例化单例的示例:

public sealed class Singleton
{ 
   private Singleton() { }

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

   private class SingletonCreator
   {
      static SingletonCreator() { }
      internal static readonly Singleton instance = new Singleton();
   }
}

用法:

Singleton s1 = Singleton.Instance;
Singleton s2 = Singleton.Instance;
if (s1.Equals(s2))
{
    Console.WriteLine("Thread-Safe Singleton objects are the same");
}

通用解决方案:

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

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

    private class SingletonCreator 
    {
        static SingletonCreator() { }

        internal static readonly T instance = new T();
    }
}

用法:

class TestClass { }

Singleton s1 = Singleton<TestClass>.Instance;
Singleton s2 = Singleton<TestClass>.Instance;
if (s1.Equals(s2))
{
    Console.WriteLine("Thread-Safe Generic Singleton objects are the same");
}

最后,这里有一个相关且有用的建议 - 为了帮助避免使用 lock 关键字可能导致的死锁,请考虑添加以下属性以帮助保护仅公共静态方法中的代码:

using System.Runtime.CompilerServices;
[MethodImpl (MethodImplOptions.Synchronized)]
public static void MySynchronizedMethod()
{
}

参考:

  1. C# Cookbook (O'Reilly)、Jay Hilyard 和 Stephen Teilhet
  2. C# 3.0 设计模式 (O'Reilly),Judith Bishop
  3. CSharp-Online.Net - 单例设计模式:线程安全的单例
于 2011-07-20T03:47:43.680 回答
1

我一直在寻找一种更好的单例模式并且喜欢这个。因此将其移植到 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
于 2009-05-15T16:00:02.687 回答
1

试试这个以线程安全和惰性的方式实现 Singleton 设计模式的通用 Singleton 类(感谢 wcell)。

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);
            }
        }
    }
}
于 2009-10-19T20:53:56.840 回答
0

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


/// <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; } } }
于 2009-05-30T09:14:50.710 回答
0

我非常喜欢您的原始答案-唯一缺少的(根据吹镖发布的链接)是使 _instance 变量易失,以确保它实际上已被设置在锁中。当我必须使用单例时,我实际上使用吹镖解决方案,但我不需要后期实例化等。

于 2008-09-19T06:55:17.393 回答
0

维基百科

单例模式是一种将类的实例化限制为一个对象的设计模式

我相信使用泛型没有保证的方法,如果你限制了单例本身的实例化,如何限制主类的实例化,我认为不可能做到这一点,并实现这个简单的模式并不难,使用静态构造函数和私有集采取这种方式:

public class MyClass
{
        private MyClass()
        {

        }

        static MyClass()
        {
            Instance = new MyClass();
        }

        public static MyClass Instance { get; private set; }
}

或者:

public class MyClass
    {
            private MyClass()
            {

            }

            static MyClass()
            {
                Instance = new MyClass();
            }

            private static MyClass instance;



            public static MyClass Instance
            {
                get
                {
                    return instance;
                }
                private set
                {
                    instance = value;
                }
            }
    }
于 2013-01-21T20:31:33.693 回答
0

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;
    }
  }
}
于 2009-05-30T09:38:15.997 回答
0
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();
}
于 2009-09-24T20:25:24.583 回答
0

前段时间看到一个使用反射来访问私有(或公共)默认构造函数:

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;
        }
    }
}
于 2009-09-24T20:34:57.010 回答
0

我将此提交给小组。它似乎是线程安全的、通用的并且遵循模式。你可以继承它。这是从其他人所说的拼凑而成的。

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

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

    public static T Instance
    {
        get { return SingletonCreator.Instance; }
    }
}

预期实施:

public class Foo: Singleton<Foo>
{
     private Foo() { }
}

然后:

Foo.Instance.SomeMethod();
于 2011-10-09T13:31:14.307 回答
0

这对我有用:

public static class Singleton<T> 
{
    private static readonly object Sync = new object();

    public static T GetSingleton(ref T singletonMember, Func<T> initializer)
    {
        if (singletonMember == null)
        {
            lock (Sync)
            {
                if (singletonMember == null)
                    singletonMember = initializer();
            }
        }
        return singletonMember;
    }
}

用法:

private static MyType _current;
public static MyType Current = Singleton<MyType>.GetSingleton(ref _current, () => new MyType());

使用单例:

MyType.Current. ...
于 2017-09-14T00:58:11.070 回答
0

无论您选择哪个示例,始终使用 Parallel.For 检查并发性!(迭代可以并行运行的循环)

放入 Singleton C'tor :

 private Singleton ()
        {
            Console.WriteLine("usage of the Singleton for the first time");
        }

放入 Main :

Parallel.For(0, 10,
              index => {
                  Thread tt = new Thread(new ThreadStart(Singleton.Instance.SomePrintMethod));
                  tt.Start();
              });
于 2018-07-04T08:39:19.983 回答
-8

您不需要所有这些,C# 已经内置了一个很好的单例模式。

static class Foo

如果您需要比这更有趣的东西,那么您的新单例很可能会变得足够不同,以至于您的通用模式将毫无用处。

编辑:通过“任何更有趣的”我包括继承。如果你可以从单例继承,它就不再是单例了。

于 2008-09-19T07:02:49.403 回答