简体   繁体   中英

Static constructor - Singleton Design pattern in c#

What if, I replaced private constructor with a static constructor in singleton Design pattern?

public sealed class Singleton
{
    private static Singleton instance=null;

    private Singleton()
    {
    }

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

Static constructor will be called only once and I couldn't find any difference in the implementation. Can we replace private with static constructor?

All that the private constructor is really doing in this case is preventing anything outside of the class from instantiating an instance of class Singleton, which is almost certainly intentional as a singleton should only have a single instance.

Static class constructors are run once for a type, at an unknown time, before the type, or any of it's static members, is to be utilized. Static fields are initialized before the static constructor would be run.

So, I suppose you could replace the constructor with a static one, but that would then give you the implicit parameter-less constructor on the Singleton Type, which would allow anyone to instantiate an instance, which is likely at odds with why you are using the singleton pattern in the first place. It also wouldn't change anything about how your class was being constructed, really, so why do it?

As an example, take the following class as an example:

public class Test { }

Under the covers, because there is no declared constructor, the C# compiler implicitly adds a parameterless, public constructor to the class, allowing consumers to create an instance.

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

This is all fine and good if you want to be able to make instances of your class. The singleton pattern intends to only provide a single instance of a type to the consumers. We could add this static instance to our test type like so:

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

and we would be able to get this static instance like so:

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

So we are providing access to our singleton object through it's instance field, as expected, but we can still create instances of the singleton type, which is less good, as it goes against the purpose of a singleton (namely, having only a single shared instance.)

So we add a private, parameterless constructor to the type.

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

Adding a constructor to a type will cause the C# compiler not to add an implicit public parameter-less constructor. Making it private allows it to be accessed within the class scope, which is used for instantiating our instance property, and prevents anything else from instantiating the object. The end result being:

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

Your singleton object now prevents other instances of the class from being instantiated, and the only way to use it is through the instance property as intended.

In simple terms, if you remove the private constructor, then anyone will be able to create a new instance of Singleton :

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

And if anyone can instantiate Singleton as above, then you no longer have a singleton.

Another cleaner way to do it is to use readonly on you private instance.

This is less code and also thread safe. The CLR takes care of everything for you, no need for lock , check for null and stuff.

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

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

    private Singleton()
    {
    }
}

Then simply test:

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

EDIT:

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

In simplest terms, if you remove private , the default public constructor will get exposed. Then outsiders will be allowed to use new Singleton(); and make number of instances of Singleton class. So no Singleton Pattern will be there.

Additionally this classic implementation of Singleton pattern (private constructor + static getInstance() with either lazy-loading or eager loading) is so evil . In modern day you must switch to a Dependency-Injection framework instead.

This should work just fine. You could also make the class static and generic so you can store whatever kind of value in instance you want it to hold. This would facilitate the separation of concerns, keeping the singleton pattern and the class that it will contain separate.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM