c# – Singleton и static class

Question:

So I decided to see what kind of miracle this Singleton is. And the question immediately arose: why is it better than a static class? Objects cannot be made from class statics, and you cannot inherit from it either.
And in general, what is the scope of the singleton?
In a multithreaded application, will you have problems using class statics?

For example, here are two identical classes, but implemented as a singleton and statics:

public sealed class MyMathSingleton
{
    private static readonly Object obj_lock = new Object();
    private static MyMathSingleton instance = null;

    private MyMathSingleton()
    { }

    public static MyMathSingleton Instance
    {
        get
        {
            if (instance != null) return instance;
            Monitor.Enter(obj_lock);
            Interlocked.Exchange(ref instance, new MyMathSingleton());
            Monitor.Exit(obj_lock);
            return instance;                
        }
    }

    public int A_plus_B(int a, int b)
    { return a + b; }
}

public static class MyMathStatic
{
    public static int A_plus_B(int a, int b)
    { return a + b; }
}  

PS. I took the implementation of the singleton from here: Three ages of the Singleton pattern . True, the author writes there that the creation of an instance should look like this, otherwise a miracle will not happen:

Singleton temp = new Singleton();
Interlocked.Exchange(ref instance, temp);   

And for me, such a record is quite normally eaten by the compiler:

Interlocked.Exchange(ref instance, new MyMathSingleton());  

UPD 14.05.27

Suppose I have a form in my application, with the help of which a new record is entered into one of the database tables, or the record is being edited. I only need this form, I will say more – if there are several of them, then problems may arise when editing the record. In this case, is it advisable to implement the form class as a singleton?

Answer:

Re: your PS

The author of the article on Habré self-confidently smacks nonsense.

Let's start with the main thing: his singleton code is not thread safe ! Parsing:

if(instance != null) return instance;
// (*)
Monitor.Enter(s_lock);
Singleton temp = new Singleton();
Interlocked.Exchange(ref instance, temp);
Monitor.Exit(s_lock);
return instance;

Let thread 1 start executing the above code. instance is null at this point, so the first check passes, and execution reaches the line (*).

Let the context switch happen at this moment (it's possible, right?), And the second thread begins to execute. It checks instance in the same way, passes the initial check, runs through (*), gets a lock , creates a singleton instance, writes it to instance , releases lock and exits. The second thread gets a reference to the instance of the newly created singleton.

The first thread now takes over. He gets lock in the same way, creates a second instance of the singleton, writes it to instance , overwriting the old value, releases lock and exits. The first thread receives a link to another object.

Catastrophe.

Now for the little things. .NET developers are not just made lock(obj) synonymous Monitor.Enter(obj, ref lockTaken) , and not just Monitor.Enter(obj) . The Monitor.Enter(obj) variant does not work correctly in case there are exceptions. Therefore, replacing lock with an explicit Monitor.Enter is a deterioration.

Even worse is the rejection of try / catch. Without a bazaar, without try / catch, it's faster, only the code is wrong. (However, if we don't need the correct code, it would be even faster without lock .) If, for some reason, the Singleton constructor Singleton exception (and this can be not only division by 0, but also a TypeLoadException , for example), then s_lock will remain s_lock forever – deadlock.

Then, the author criticizes the correct, recommended by experts (and Jon Skeet is a mega-expert) implementations under completely contrived pretexts. Regarding the "via readonly field" approach, the author writes:

The constructor can only be static. This is a feature of the compiler – if the constructor is not static, then the type will be marked as beforefieldinit and readonly will be created simultaneously with the static ones.

This is actually not true, the author apparently just did not understand what exactly Jon wrote. The singleton has a non-static constructor, just an explicit static constructor must be present . There is a huge difference between "a static constructor must be present" and "a constructor can only be static".

Correct, witty, canonical implementation of a singleton with a nested class received only the remark "It has the same drawbacks as any other code that uses nested classes." I am not aware of the drawbacks caused by the mere presence of nested classes. If anyone informs me, I'm ready to change my mind.

Finally, the most conceptually correct solution with Lazy<T> criticized for the fact that it "does not work with streams", but uses language structures that "trick the interpreter." Everything is wrong here, except for conjunctions and prepositions. To begin with, C # is a compiled language; there is no C # interpreter. Then, there is no cheating: Lazy<T> works strictly as its documentation promises. The fact that its behavior is different from that of other classes is not a trick or trick. If desired, the same can be manually implemented by yourself.

Then, the correct one when implementing a singleton is not multithreading or other technical trifles, but a simple invariant: no matter how the singleton is used, there is always no more than one instance of a singleton on the AppDomain guaranteed. This is what the Lazy<T> class guarantees. Whether it uses special mechanisms to support multithreading or properties of the language is not interesting to anyone. The main thing is that the semantics of a single copy is consistent. This is what the Lazy<T> class guarantees, abstracting us from the implementation details. And that is why such an implementation is the most correct one.

Conclusion: not all articles on Habré are equally useful.

Scroll to Top