19

对于普通的单身人士,你们怎么看?

using System;
using System.Reflection;

// Use like this
/*
public class Highlander : Singleton<Highlander>
{
    private Highlander()
    {
        Console.WriteLine("There can be only one...");
    }
}
*/

public class Singleton<T> where T : class
{
    private static T instance;
    private static object initLock = new object();

    public static T GetInstance()
    {
        if (instance == null)
        {
            CreateInstance();
        }

        return instance;
    }

    private static void CreateInstance()
    {
        lock (initLock)
        {
            if (instance == null)
            {
                Type t = typeof(T);

                // Ensure there are no public constructors...
                ConstructorInfo[] ctors = t.GetConstructors();
                if (ctors.Length > 0)
                {
                   throw new InvalidOperationException(String.Format("{0} has at least one accesible ctor making it impossible to enforce singleton behaviour", t.Name));
                }

                // Create an instance via the private constructor
                instance = (T)Activator.CreateInstance(t, true);
            }
        }
    }
}
4

6 回答 6

30

创建一个单例类只需要几行代码,并且很难制作一个通用的单例,我总是编写这些代码行。

public class Singleton
{
    private Singleton() {}
    static Singleton() {}
    private static Singleton _instance = new Singleton();
    public static Singleton Instance { get { return _instance; }}
}

private static Singleton _instance = new Singleton();

行消除了对锁定的需要,因为静态构造函数是线程安全的。

于 2008-12-19T11:45:13.627 回答
6

好吧,它并不是真正的单例——因为你无法控制T,所以可以有尽可能多的T实例。

(删除了线程竞争;注意了双重检查的用法)

于 2008-12-19T11:43:31.617 回答
5

我已经删除了我之前的答案,因为我没有注意到检查非公共构造函数的代码。但是,这是仅在执行时执行的检查 - 没有编译时检查,这是对它的打击。它还依赖于有足够的访问权限来调用非公共构造函数,这增加了一些限制。

此外,它不禁止内部构造函数——所以你最终可以得到非单例。

为了简单的线程安全,我个人也会在静态构造函数中创建实例。

基本上我不是很喜欢——创建单例类很容易,而且你不应该经常这样做。单例对于测试、解耦等来说是一种痛苦。

于 2008-12-19T11:50:39.763 回答
5

这是我使用 .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; } } 
    }

它的使用如下:

   public class Adaptor
   {
     public static Adaptor Instance { get { return Singleton<Adaptor>.Instance;}}
   }
于 2011-10-25T15:57:16.527 回答
1

合并 AndreasN 答案和 Jon Skeet 的“第四版 - 不那么懒惰,但不使用锁的线程安全”的 Singleton c# 实现,为什么不使用代码片段来完成所有艰苦的工作:

<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>Singleton Class</Title>
            <Author>TWSoft</Author>
            <Description>Generates a singleton class</Description>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
            <Keywords>
                <Keyword>Singleton</Keyword>
            </Keywords>
            <Shortcut>singleton</Shortcut>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>ClassName</ID>
                    <ToolTip>Replace with class name</ToolTip>
                    <Default>MySingletonClass</Default>
                </Literal>
            </Declarations>

            <Code Language="CSharp">
                <![CDATA[
                public class $ClassName$
                {
                    #region Singleton
                    static readonly $ClassName$ mInstance = new $ClassName$();

                    // Explicit static constructor to tell C# compiler
                    // not to mark type as beforefieldinit
                    static $ClassName$()
                    {
                    }

                    private $ClassName$()
                    {
                    }

                    public static $ClassName$ Instance
                    {
                        get { return mInstance; }
                    }
                #endregion
                }
                ]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

然后您可以将其保存到 .snippt 文件中,并将其添加到 VS IDE(工具->代码片段管理器)

于 2013-03-20T02:17:29.997 回答
0

使用泛型对单例没有用。因为您始终可以创建类型参数 T 的多个实例,然后根据定义它不是单例。

看这个:

public sealed class Singleton<T> where T : class, new()
{
    private static readonly Lazy<T> instance = new Lazy<T>(() => new T());
    public static T Instance => instance.Value;
    private Singleton() { }
}

当你像这样使用它时

public class Adapter
{
    public static Adapter Instance => Singleton<Adapter>.Instance; 
    // private Adapter(){ } // doesn't compile.
}

您仍然可以自己创建适配器,只需调用

new Adapter();

将私有构造函数添加到 Adapter 会破坏代码。请注意,此适配器使用组合而不是继承。即它不是一个单例它有一个单例。如果某物是单例,它应该派生自或实现一个接口。

public abstract class Singleton<T> 
{
    protected static Lazy<T> instance;
    public static T Instance => instance.Value;
}

public sealed class Adapter : Singleton<Adapter>
{
    static Adapter()
    {
        instance = new Lazy<Adapter>(() => new Adapter());
    }

    private Adapter() { }
}

基本上这只会将静态字段移动到通用基类中,而它不再是只读的,因此可以在初始化后进行更改。它还要求您记住添加私有构造函数,将其标记为密封并执行一些初始化,因此它仍然没有很好地封装并且容易出错。

我们可以通过在基本构造函数中添加检查来改进这一点。

public abstract class Singleton<T> where T : Singleton<T>, new()
{
    private static bool instantiated;
    private static readonly Lazy<T> instance = new Lazy<T>(() => new T());
    public static T Instance => instance.Value;
    protected Singleton()
    {
        if (instantiated)
            throw new Exception();
        instantiated = true;
    }
}

public /* sealed */ class Adapter : Singleton<Adapter>
{
}

由于 Singleton 是惰性的,模式仍然被破坏。

  new Adapter(); // this works
  Adapter.Instance; // this throws an error.

  Adapter.Instance; // this works
  // just running in production someone decided to call:
  new Adapter(); // this throws an error

此类错误只能在测试期间检测到。

我仍然更喜欢直接使用该模式,它基本上只有 3 行代码,并且在编译时是安全的。此外,当您需要不同的基类但仍需要惰性单例并要求它是真正的单例时,存在一个简单的解决方案:(请参阅:http ://csharpindepth.com/Articles/General/Singleton.aspx )

public sealed class Adapter
{
    private static readonly Lazy<Adapter> instance = new Lazy<Adapter>(() => new Adapter());
    public static Adapter Instance { get { return instance.Value; } }
    private Adapter() { }
}

您不能将其正确地重构为不能像上面研究的方法那样被滥用的通用单例。

于 2016-08-17T09:57:16.190 回答