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?
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?
In C #, there are several ways to initialize fields:
In the place of announcement
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!
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.
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.
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.
There are two guarantees for invoking static constructors:
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
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 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.
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.
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.
Source: https://ru.stackoverflow.com/questions/615736/
All Articles