C# 中的单例是啥?

Posted

技术标签:

【中文标题】C# 中的单例是啥?【英文标题】:What is a singleton in C#?C# 中的单例是什么? 【发布时间】:2011-01-10 11:20:17 【问题描述】:

什么是单例,我应该什么时候使用它?

【问题讨论】:

可能重复:***.com/questions/246710/… 另外,Singleton 是 OO 编程中最广泛使用和滥用的设计模式之一。 @F***o:因为它有一种创建毫无意义的耦合的方法(我怎样才能让XY 交谈?只需让Y 成为单身人士!),其中反过来会导致测试/调试困难和编程风格。有时单例是必要的;大多数时候,不是。 这是我的标准电话面试问题之一。正确答案是:从不。 @jonnii 很好,它有助于警告潜在的开发人员那里的老板是什么样的! 【参考方案1】:

单例是一个类,它只允许创建自己的一个实例 - 并提供对所述实例的简单、轻松的访问。单例前提是跨软件开发的一种模式。

有一个 C# 实现 "Implementing the Singleton Pattern in C#" 涵盖了您需要了解的大部分内容 - 包括一些关于线程安全的好建议。

说实话,很少需要实现单例 - 在我看来,它应该是你应该注意的事情之一,即使它不经常使用。

【讨论】:

不错的教程,但天哪,他们对代码缩进做了什么 这里有一个更直接的链接,指向我认为是 2020 年的理想实施。即“using .NET 4's Lazy<T> type”,以及指向 Microsoft Doc 的Lazy&lt;T&gt; Class 的链接。 【参考方案2】:

您要求使用 C#。简单的例子:


public class Singleton

    private Singleton()
    
        // Prevent outside instantiation
    

    private static readonly Singleton _singleton = new Singleton();

    public static Singleton GetSingleton()
    
        return _singleton;
    

【讨论】:

不是线程安全的。两个线程可以同时调用并且可以创建两个单独的对象。 @Alagesan Palani,确实你是对的。我不精通类级初始化的低级细节,但我认为我所做的更改解决了线程安全问题。 当然,我不是说你错了。我在给读者一个关于线程安全的提示,以便他们在必须处理它时会小心。 不,我认为您的评论很重要。假设一个单例应该交付一个——而且只有一个——实例,这里的竞争条件打开了交付多个实例的可能性。现在查看带有静态字段初始化的版本。如果我正确阅读了the docs 和this SO answer,我相信这可以解决线程安全问题。 它仍然不是线程安全的。将字段更改为静态不会使其成为线程安全的。您需要添加一个静态的构造函数,然后它将是线程安全的。【参考方案3】:

它是什么: 在应用程序的整个生命周期中只有一个持久实例的类。见Singleton Pattern。

什么时候应该使用它:尽可能少。只有当你绝对确定你需要它时。我不愿意说“从不”,但通常有更好的选择,例如依赖注入或简单的静态类。

【讨论】:

我不确定静态类是不是比单例更好的选择......这真的取决于情况和语言。 静态类的行为方式与单例不同,单例可以作为参数传递给方法,而静态类则不能。 同意 marcgg - 我不认为静态类是单例的好替代品,因为您仍然存在提供替代品的问题,例如在测试依赖于此类的组件期间。但我也看到了不同的用途,静态类通常用于独立于状态的独立实用程序函数,其中单例是实际的类实例,它通常会存储状态。我完全同意改用 DI,然后告诉您的 DI 容器您希望它只使用该类的单个实例。 我对这个答案投了反对票,因为它没有给我关于何时使用它的信息。对于刚接触单身人士的人来说,“仅当您需要时”并没有真正为我提供任何信息。 @Adkins:DI 代表依赖注入,即通过(通常)构造函数或公共属性传入任何类依赖项。 DI 本身并不能解决“距离”问题,但它通常与知道如何自动初始化任何依赖项的控制反转 (IoC) 容器一起实现。因此,如果您正在创建一个 Singleton 来解决“X 不知道如何查找/与 Y 对话”问题,那么 DI 和 IoC 的组合可以通过更松散的耦合来解决相同的问题。【参考方案4】:

在c#中实现单例的另一种方式,我个人更喜欢这种方式,因为您可以将单例类的实例作为属性而不是方法来访问。

public class Singleton
    
        private static Singleton instance;

        private Singleton()  

        public static Singleton Instance
        
            get
            
                if (instance == null)
                    instance = new Singleton();
                return instance;
            
        

        //instance methods
    

但是,据我所知,这两种方式都被认为是“正确的”,所以这只是个人风格的事情。

【讨论】:

不是线程安全的。两个线程可以同时调用并且可以创建两个单独的对象。【参考方案5】:
using System;
using System.Collections.Generic;
class MainApp

    static void Main()
    
        LoadBalancer oldbalancer = null;
        for (int i = 0; i < 15; i++)
        
            LoadBalancer balancerNew = LoadBalancer.GetLoadBalancer();

            if (oldbalancer == balancerNew && oldbalancer != null)
            
                Console.WriteLine("0 SameInstance 1", oldbalancer.Server, balancerNew.Server);
            
            oldbalancer = balancerNew;
        
        Console.ReadKey();
    


class LoadBalancer

    private static LoadBalancer _instance;
    private List<string> _servers = new List<string>();
    private Random _random = new Random();

    private static object syncLock = new object();

    private LoadBalancer()
    
        _servers.Add("ServerI");
        _servers.Add("ServerII");
        _servers.Add("ServerIII");
        _servers.Add("ServerIV");
        _servers.Add("ServerV");
    

    public static LoadBalancer GetLoadBalancer()
    
        if (_instance == null)
        
            lock (syncLock)
            
                if (_instance == null)
                
                    _instance = new LoadBalancer();
                
            
        

        return _instance;
    

    public string Server
    
        get
        
            int r = _random.Next(_servers.Count);
            return _servers[r].ToString();
        
    

我从dofactory.com 获取代码,没什么特别的,但是我发现这比 Foo 和 Bar 的示例好得多 另外 Judith Bishop 关于 C# 3.0 设计模式的书有关于 mac 中的活动应用程序的示例码头。

如果您查看代码,我们实际上是在 for 循环上构建新对象,这样会创建新对象但会重用实例,因此 oldbalancer 和 newbalancer 具有相同的实例,如何?其原因在于函数 GetLoadBalancer() 上使用了 static 关键字,尽管服务器值不同,即随机列表,但 GetLoadBalancer() 上的静态值属于类型本身而不是特定对象。

这里还有double check locking

if (_instance == null)
            
                lock (syncLock)
                
                    if (_instance == null)

来自 MSDN

lock 关键字确保一个线程不进入代码的临界区,而另一个线程在临界区。如果另一个线程试图输入一个锁定的代码,它会等待、阻塞,直到对象被释放。

所以每次互斥锁都会发出,即使它不需要,也是不必要的,所以我们有空检查。

希望它有助于清除更多。

如果我的理解方向错误,请发表评论。

【讨论】:

【参考方案6】:

单例(这与 C# 无关,它是一种 OO 设计模式)是指您希望在整个应用程序中只允许创建一个类的实例。使用通常包括全球资源,尽管我会根据个人经验说,它们通常是巨大痛苦的根源。

【讨论】:

【参考方案7】:

虽然单例只能有一个实例,但它与静态类不同。静态类只能包含静态方法并且永远不能被实例化,而单例的实例可以像任何其他对象一样使用。

【讨论】:

【参考方案8】:

这是一种设计模式,并不特定于 c#。有关它的更多信息,请访问互联网和 SO,例如 wikipedia article。

在软件工程中,单例 模式是一种设计模式 用于限制 a 的实例化 归类为一个对象。这很有用 当只需要一个对象时 协调整个系统的行动。 这个概念有时被概括 运行更多的系统 当只有一个对象时有效 存在,或限制 实例化到一定数量的 对象(例如,五个)。有人认为 一个反模式,判断它是 过度使用,引入不必要的 在唯一的情况下的限制 类的实例实际上不是 必需,并引入全局状态 进入应用程序。

如果你想要一个只能实例化一次的类,你应该使用它。

【讨论】:

【参考方案9】:

我用它来查找数据。从数据库加载一次。

public sealed class APILookup
    
        private static readonly APILookup _instance = new APILookup();
        private Dictionary<string, int> _lookup;

        private APILookup()
        
            try
            
                _lookup = Utility.GetLookup();
            
            catch  
        

        static APILookup()
                    
        

        public static APILookup Instance
        
            get
            
                return _instance;
            
        
        public Dictionary<string, int> GetLookup()
        
            return _lookup;
        

    

【讨论】:

【参考方案10】:

什么是单例: 它是一个类,它只允许创建它自己的一个实例,并且通常提供对该实例的简单访问。

什么时候应该使用: 视情况而定。

注意:请不要在数据库连接上使用,详细答案请refer@Chad Grant 的回答

这是Singleton的简单示例:

public sealed class Singleton

    private static readonly Singleton instance = new Singleton();

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Singleton()
    
    

    private Singleton()
    
    

    public static Singleton Instance
    
        get
        
            return instance;
        
    

您也可以使用Lazy&lt;T&gt; 创建您的Singleton

See here 使用Lazy&lt;T&gt; 的更详细示例

【讨论】:

【参考方案11】:

这里是单例:http://en.wikipedia.org/wiki/Singleton_pattern

我不懂 C#,但实际上所有语言都一样,只是实现不同。

如果可能,您通常应该避免使用单例,但在某些情况下它非常方便。

对不起我的英语;)

【讨论】:

你的英语还行:)【参考方案12】:

我知道现在回答这个问题已经很晚了,但是使用 Auto-Property 你可以这样做:

public static Singleton Instance get; = new Singleton();

Singleton 是你的类并且可以通过,在本例中为只读属性 Instance

【讨论】:

【参考方案13】:

线程安全单例,不使用锁和惰性实例化。

此实现具有静态构造函数,因此每个应用程序域仅执行一次。

public sealed class Singleton


    static Singleton()

    private Singleton()

    public static Singleton Instance  get;  = new Singleton();


【讨论】:

【参考方案14】:

E.X 可以使用 Singleton 来获取需要注入的全局信息。

就我而言,我将记录的用户详细信息(用户名、权限等)保存在全局静态类中。当我尝试实现单元测试时,我无法将依赖项注入到 Controller 类中。因此,我已将我的静态类更改为单例模式。

public class SysManager

    private static readonly SysManager_instance = new SysManager();

    static SysManager() 

    private SysManager()

    public static SysManager Instance
    
        get return _instance;
    

http://csharpindepth.com/Articles/General/Singleton.aspx#cctor

【讨论】:

【参考方案15】:

来自Implementing the Singleton Pattern in C#的第四版

您可以使用此实现(并且仅此实现)的一个捷径是使实例成为公共静态只读变量,并完全摆脱该属性。这使得基本的骨架代码非常小!

public sealed class Singleton

    public static readonly Singleton Instance = new Singleton();

    static Singleton()
    
    

    private Singleton()
    
    

【讨论】:

以上是关于C# 中的单例是啥?的主要内容,如果未能解决你的问题,请参考以下文章

ruby 中的单例类到底是啥?

iOS中的单例对象[重复]

python中的单例模式

C#中的单例实现问题

通过枚举方式的单例是惰性初始化的吗?

Unity3D中的单例模式