I understand that the static constructor is used to assign values ​​to static variables, that it is called first when creating a class object.

But why is it needed if I can assign these values ​​when declaring variables?

  • Most likely, the assignment of variables at the place of declaration is syntactic sugar, and the assignments themselves are actually made in the static constructor. - ߊߚߤߘ
  • But what does it do without a static constructor ? do not manually declare it? or so that .NET doesn't create it? - Grundy
  • do not declare manually, but simply assign a value to a static variable at the time of its declaration.
  • one
    @Qwertiy, Do not hesitate :-) - Grundy
  • one
    @Qwertiy, although it doesn’t really seem to be in all, but in those where there are static fields :) - Grundy

6 answers 6

In C #, there are several ways to initialize fields:

  1. In the place of announcement

  2. In the constructor

Here is a naive example:

class Foo { private string s1 = "s1"; private string s2; private static string s3 = "s3"; private static string s4; public Foo() { s2 = "s2"; } static Foo() { s4 = "s4"; } } 

When viewed from a bird's-eye view, the initialization in place is implemented quite simply: the expression used to initialize the field is transferred to the corresponding constructor — to the instance constructor for the instance fields and to the static constructor for the static fields.

Here we must say why we have two designers. An instance constructor is some helper function designed to initialize an instance of the object being created. For example, we can say that any valid object must have some behavior (invariant) and the constructor is a special function that must provide it. An invariant can be anything: from the fact that some field is not zero, ending with more complex rules, for example, that the sum of дебет and кредит fields is 0.

In addition to the invariants of objects, there are also invariants of the type: some conditions that must be true not for specific objects, but for the type as a whole. Type behavior is expressed by static members, which means that the type “invariant” is a valid state of static variables, the static constructor being responsible for the validity of which.

Unlike the object constructor, the type constructor is not called by the user. Instead, it is called by the CLR at the time of the first call to the type (we omit the exact rules).

There are subtle differences in the behavior at runtime, which distinguishes the use of the field initializer at the place of declaration from the initialization of the same fields in the constructor. Static and instance field initializers are syntactic sugar, but it is very important to know which one!

Difference in the case of instance fields and constructors

All initializers of instance fields are moved by the C # compiler into the instance constructor. But the main question here: where exactly.

In general, the instance constructor looks like this:

 // ctor // Код инициализаторов полей // Вызов конструктора базового класса // Код текущего конструктора 

This algorithm has two important consequences. First, the code of initializers of instance fields is not just placed in the constructor, it is placed at the very beginning, even before the call to the constructor of the base class , and secondly, it will be copied to all the constructors .

The first remark is very important (yes, they can ask about it during the interview and this can be useful in real applications). For example, if someone wants to call a virtual method in the constructor of the base class, then some of the fields will be initialized, and some will not. It is easy to guess that the fields initialized in the ad space will already be valid, and other fields will contain default values.

Yes, yes, yes, calling virtual methods in the constructors of the base class is bad, but in reality it happens and you need to understand what will happen in runtime in this case.

Difference in the case of static fields and constructors

With static designers, things are somewhat more complicated and simpler at the same time. From the point of view of inheritance, the method of initializing static fields does not intersect with the call of the static constructor of the base class. No There generally process of a call of static designers differs from copy. For example, at creation of a copy of the successor in the beginning the static designer of the successor, and then the static designer of base class is caused. And if the static method of the successor is twitching, then the static constructor of the base class will not be called at all by the automaton (it will be called only if the static method of the heir somehow jerks the base type).

But the difficulty arises when exactly the static constructor will be called.

As already written by @Qwertiy, the presence or absence of a static constructor in a class affects when this constructor is called. The presence of a static constructor leads to the generation of a strange special flag, which then tells the CLR that it is possible to relate more freely to the call time of the static constructor, and this can now be done not right before the first call, but, for example, before calling the method in which this call going on.

Potentially, this may affect the efficiency of the application, since now the check will be done once, rather than thousands of times, provided that the first call is in a cycle from 0 to 1000.

Conclusion

The field initializer (static and not) is sugar, but it can be bitter if you do not understand what its excessive use leads to.

I usually use the following rule of thumb. For instance fields: you need to initialize the field with the argument of the constructor - (without variants) I use the constructor; otherwise, a field initiator. In the case of static fields: in the overwhelming number of cases I use an initializer. If there is a lot of code, I select a method.

If I need to use a static constructor to set the initialization order or change the semantics of the type initialization, then I add a huge comment that says why you need to be very careful with this code fragment.

If I need to use an initializer for instance fields so that the initialization takes place before calling the constructor of the base class, then I refactor the code so that it is not needed. For example, select the factory method. If such behavior is really needed, then a two-page comment is needed, which explains why it is needed and why other options are not suitable.

  • small side question: does any class have a static constructor? or only for those where there are static fields for example or is it described manually? - Grundy
  • This is a side question, but not an easy one. It seems to me that there will be no static constructor if there are no static variables. And if there was, then it would have cut JIT, if it were empty. - Sergey Teplyakov
  • For some reason it seemed to me that I had seen somewhere that he should have been with everyone, but in the test example, ildasm really showed it only in those classes in which there are static fields - Grundy
  • one
    Hello! Could you clarify the situation with this issue ? - Grundy
  • one
    @Grundy Answered. I completely disagree with the answer adopted there :) - Sergey Teplyakov

There are two guarantees for invoking static constructors:

  • Basic type initialization guarantee: the type constructor will be called before creating an instance or before the first call to a static member.
  • “Relaxed” (relaxed) type initialization guarantee (BeforeFieldInit): the type constructor will be called when the static field is first accessed (valid for .NET 4.0+) and can be called anytime before the first type call.

In order for the CLR to use the “relaxed” model, the type must be flagged with the BeforeFieldInit flag. For the C # language, the choice is determined by the presence or absence of an explicit static constructor: in the presence of an explicit static constructor, the basic model of type initialization is used, and in the absence of a static constructor, the relaxed model is used.

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

Practice shows that in most cases, in the absence of an explicit constructor, the JIT compiler calls the initializer of static variables immediately before calling a method that uses this variable.

If we use the static constructor of the class Singleton, then the behavior will be exactly the same that most developers expect - the field initializer will not be called until the first call.

Sources:
http://sergeyteplyakov.blogspot.ru/2013/07/blog-post.html
http://sergeyteplyakov.blogspot.ru/2011/08/blog-post.html
http://sergeyteplyakov.blogspot.ru/2013/06/blog-post_28.html

  • When you start the application, there will be only one line on the screen: “Starting Main ...”. - it looks like something is missing in the code. - Grundy
  • @Grundy, fixed. - Qwertiy
  • 3
    I answered, but your answer is quite valid and useful. I deliberately did not paint the aspect covered in your answer in detail. - Sergey Teplyakov
  • @SergeyTeplyakov, here is all the information from your blog - I expected you to want to write it in your answer. Well, if not, then let it remain :) - Qwertiy

Not every object can be initialized to a single line. For example, the XmlSerializerNamespaces class that I often use requires after calling a constructor to call the Add method several times.

Or, if you need to build a delegate through Linq Expressions, you have to create parameters separately, although the rest of the expression can usually be written in one line.

Sometimes more complex initialization logic is required.


In addition, you need to understand that in C # the initializers of static fields differ from the static constructor. In the compilation process, they are all simply written on its beginning.

  • The idea of ​​additional actions during initialization is clear. Thank. That is, if I have a single-line initialization (assigning a value or the like), then you can do without a constructor and this will not be a bad form, right?
  • one
    @ nikola yes, you can do without a static constructor in C # code . By the way, it will be a bad form to create a static constructor where the initializer handles it. In the bytecode, the same static constructor will be present anyway :) - Pavel Mayorov

The essence of the static constructor is that it is called only once before creating the first element of the specified type. Of course, you can do initialization every time you create an instance with an additional check, but why?

But why is it needed if I can assign these values ​​when declaring variables?

It can be useful if the order of initialization of static fields is important or for performing some additional actions that do not occur during simple initialization of the type for which the static member is used.

For example :

 using System; class A { public A() { Console.WriteLine("A"); } } class B { public B() { Console.WriteLine("B"); } } public class Test { static readonly A a; static readonly B b; public static void Main() { } static Test() { b = new B(); Console.WriteLine("-"); a = new A(); } } 

So we changed the order of creation of static fields and added an additional action, which could not have been achieved with simple initialization in place.

  • I didn’t quite understand why to initialize it every time, if I simply assign a value to a static field at the moment of its declaration? The question is, why should I create a constructor if I can assign a value right away?
  • @nikola, alexolut, I thought that you want to initialize the field not at the announcement, but in the usual constructor - Grundy
  • @ nikola yes, Grundy correctly described my thoughts. I added the answer. - αλεχολυτ
  • @alexolut there below user2455111 says that it is generated automatically by the compiler with a simple assignment, is that true? And I did not think about additional actions and the order of initialization. Thank!
  • @nikola according to the answer Qwertiy, user2455111 is wrong. - αλεχολυτ

If we consider a static class as a template, then this is a "free" singleton, and all static members of the class are members of the given singleton. At the same time, if a static class is transferred to the instance of a non-static class, it will have full access to the fields of this non-static class.

A static class is also called a TYPE class (and a static constructor is a type constructor, respectively), which means that even if the class is declared not static, a static component will in fact be created for it.

Well, then we must proceed from the convenience and necessity:

STATIC CLASS "INSTANCE": 1. No inheritance 2. Available everywhere without transmitting any reference to the function code (according to the scope)

A COPY OF A NOT STATIC CLASS: 1. There is inheritance 2. You can have multiple instances 3. Available only by reference passed to the function

    For example, do you have a class

     internal class SomeType { private static Int32 s_x = 5; } 

    this code will be equivalent to code

     internal class SomeType { private static Int32 s_x; static SomeType() { s_x = 5; } } 

    If you use the 1st option, the compiler will automatically generate a type constructor.

    • Not exactly equivalent. - Qwertiy
    • @Qwertiy Why? - user2455111
    • See my answer and 3 articles on the links. - Qwertiy
    • @Qwertiy I assumed that the code would be equivalent, and not the moment at which it is executed. And by the way, I tried to bring your answer to the code. The results were as follows: With the basic guarantee, the variable was assigned a value at the beginning of the execution of the static method of the Main method (or when the constructor of the class in which the variable will be used) is executed; when relaxed, when the class is initialized, before executing the method (using the variable), before calling variable - user2455111