C# 一个通用的单例

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/380755/
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-04 01:19:24  来源:igfitidea点击:

A generic singleton

c#singletongenerics

提问by Simon Hughes

What do you guys think about this for a generic singleton?

你们对通用单身人士有什么看法?

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

采纳答案by AndreasN

Creating a singleton class is just a few lines of code, and with the difficulty of making a generic singleton i always write those lines of code.

创建一个单例类只是几行代码,由于很难制作一个通用的单例,我总是写这些代码行。

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

The

private static Singleton _instance = new Singleton();

line removes the need for locking, as a static constructor is thread safe.

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

回答by Marc Gravell

Well, it isn't really singleton - since you can't control T, there can be as many Tinstances as you like.

好吧,它并不是真正的单例 - 因为您无法控制T,所以可以有任意数量的T实例。

(removed thread-race; noted the double-checked usage)

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

回答by Jon Skeet

I've deleted my previous answer as I hadn't noticed the code which checks for non-public constructors. However, this is a check which is only performed at execution time - there's no compile-timecheck, which is a strike against it. It also relies on having enough access to call the non-public constructor, which adds some limitations.

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

In addition, it doesn't prohibit internalconstructors - so you can end up with non-singletons.

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

I'd personally create the instance in a static constructor for simple thread safety, too.

我也会亲自在静态构造函数中创建实例以实现简单的线程安全。

Basically I'm not much of a fan - it's pretty easy to create singleton classes, and you shouldn't be doing it that often anyway. Singletons are a pain for testing, decoupling etc.

基本上我不太喜欢 - 创建单例类非常容易,无论如何你不应该经常这样做。单例对于测试、解耦等来说是一种痛苦。

回答by Alexandr

This is my point using .NET 4

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

and it's using is following:

它的使用如下:

   public class Adaptor
   {
     public static Adaptor Instance { get { return Singleton<Adaptor>.Instance;}}
   }

回答by Luis Vaccaro

Merging AndreasN answer and Jon Skeet's "Fourth version - not quite as lazy, but thread-safe without using locks" of a Singleton c# implementation, why don't use a code snippet to do all the hard work:

合并 AndreasN 答案和 Jon Skeet 的“第四版 - 不是很懒惰,但不使用锁的线程安全”的单例 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>

Then you can save this into a .snippt file, and add it to VS IDE (Tools->Code Snippets Manager)

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

回答by Wouter

Using generics is not useful for singletons. Because you can always create multiple instances of the type parameter T and then it is notby definition a singleton.

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

Look at this:

看这个:

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

And when you use it like this

当你像这样使用它时

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

you can still create Adapters yourself just call

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

new Adapter();

Adding the private constructor to Adapter breaks the code. Note that this Adapter uses composition not inheritance. I.e. it is not a Singleton it has a Singleton. If something is a Singleton it should derive from or implement an interface.

将私有构造函数添加到 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() { }
}

Basically this only moves a static field into a generic base class while it is no longer readonly and therefore can be changed after initialization. Also it requires you to remember to add the private constructor, mark it as sealed and perform some initialization so it still isn't well encapsulated and prone to mistakes.

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

We can improve this by adding a check in the base constructor.

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

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

Due to the fact that the Singleton is lazy the pattern is still broken.

由于单例是懒惰的,模式仍然被打破。

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

also

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

These kind of error's can only be detected during testing.

这些类型的错误只能在测试期间检测到。

I would still prefer to just use the pattern directly it's basically just 3 lines of code and it is compile time safe. Also when you need a different base classes but still need a lazy singleton and require it to be a true singleton a simple solution exists: (see: http://csharpindepth.com/Articles/General/Singleton.aspx)

我仍然更愿意直接使用该模式,它基本上只有 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() { }
}

You cannot refactor this properly into a generic singleton that cannot be misused like the above investigated approaches.

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

回答by Mehdi Dehghani

There is a problem with a generic singleton factory, since it's generic, you don't control the singletontype that is instantiated, so you can never guaranteethat the instance you create will be the only instance in the application.

泛型单例工厂存在一个问题,因为它是泛型的,您无法控制singleton实例化的类型,因此您永远无法保证您创建的实例将是应用程序中的唯一实例。

So, you cannot create a generic singleton factory - it undermines the pattern itself.

所以,你不能创建一个通用的单例工厂——它破坏了模式本身。