静态构造函数——c#中的单例设计模式

本文关键字:单例 设计模式 构造函数 静态 | 更新日期: 2023-09-27 18:04:03

如果我在单例设计模式中用静态构造函数代替私有构造函数会怎样?

public sealed class Singleton
{
    private static Singleton instance=null;
    private Singleton()
    {
    }
    public static Singleton Instance
    {
        get
        {
            if (instance==null)
            {
                instance = new Singleton();
            }
            return instance;
        }
    }
}

静态构造函数将只被调用一次,我在实现中找不到任何区别。我们可以用静态构造函数代替私有构造函数吗?

静态构造函数——c#中的单例设计模式

在这种情况下,私有构造函数真正做的是防止类外部的任何东西实例化Singleton类的实例,这几乎可以肯定是有意为之,因为Singleton应该只有一个实例。

静态类构造函数在类型或其任何静态成员被使用之前,在未知的时间为类型运行一次。静态字段在运行静态构造函数之前初始化。

所以,我认为你可以用一个静态的构造函数来代替这个构造函数,但是这样就会给你Singleton类型上的隐式无参数构造函数,这将允许任何人实例化一个实例,这可能与你最初使用Singleton模式的原因不一致。实际上,它也不会改变类的构造方式,那么为什么要这样做呢?

以以下类为例:

public class Test { }

在底层,因为没有声明的构造函数,c#编译器隐式地为类添加了一个无参数的公共构造函数,允许消费者创建实例。

public class Program {
    public static void Main() { 
        var test = new Test();
    }
}

如果您希望能够创建类的实例,那么这一切都很好。单例模式打算只向使用者提供类型的单个实例。我们可以像这样将这个静态实例添加到测试类型中:

public class Test { public static Test Instance {get;} = new Test(); }

,我们可以得到这样的静态实例:

public class Program {
    public static void Main() {
        var test = Test.Instance; // good
        var other = new Test(); // less than ideal
    }
}

所以我们通过它的实例字段提供了对单例对象的访问,正如预期的那样,但是我们仍然可以创建单例类型的实例,这就不太好了,因为它违背了单例的目的(即只有一个共享实例)

因此,为该类型添加一个私有的无参数构造函数。

public class Test { 
    private Test() {}
    public static Test Instance {get;} = new Test(); 
}

向类型添加构造函数将导致c#编译器不添加隐式公共无参数构造函数。将其设为私有允许在类作用域中访问它,该类作用域用于实例化我们的instance属性,并防止其他任何东西实例化该对象。最终结果是:

public class Program {
    public static void Main() {
        var test = Test.Instance; // good
        var other = new Test(); // Compile time error
    }
}

你的单例对象现在可以防止该类的其他实例被实例化,使用它的唯一方法是通过instance属性。

简单来说,如果您删除了私有构造函数,那么任何人都可以创建Singleton的新实例:

// With the private constructor, the compiler will prevent this code from working.
// Without it, the code becomes legal.
var newInstance = new Singleton();

如果有人可以像上面那样实例化Singleton,那么你就不再有一个单例了。

另一种更干净的方法是在您的私有实例上使用readonly

这是更少的代码和线程安全。CLR为你照顾一切,不需要lock,检查null和东西。

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

然后简单地测试:

[TestMethod]
public void IsSingleton()
{
    Assert.AreSame(Singleton.Instance, Singleton.Instance);
}
编辑:

示例使用lock

public sealed class Singleton
{
    private static readonly object _lock = new object();
    private static Singleton instance = new Singleton();
    public static Singleton Instance
    {
        get
        {
            lock(_lock)
            {
                if (instance==null)
                {
                    instance = new Singleton();
                }
                return instance;    
            }
        }
    }
    private Singleton()
    {
    }
}

简单来说,如果删除private,默认的公共构造函数将暴露。然后外人将被允许使用new Singleton();并创建一些Singleton类的实例。所以这里没有单例模式。

另外,这个经典的单例模式(private constructor + static getInstance() with either lazy-loading or eager loading)的实现是如此的邪恶。在现代,你必须改用依赖注入框架。

这应该可以正常工作。您还可以使类静态和泛型,这样您就可以在instance中存储任何类型的值。这将促进关注点分离,保持单例模式和它将包含的类分离。