Trying to understand static constructors

I am trying to understand the need for static constructors. No information I found answered the next question that I have. Why would you do this?

class SimpleClass { // Static variable that must be initialized at run time. static readonly long baseline; // Static constructor is called at most one time, before any // instance constructor is invoked or member is accessed. static SimpleClass() { baseline = DateTime.Now.Ticks; } } 

by contrast

 class SimpleClass { // Static variable that must be initialized at run time. static readonly long baseline = DateTime.Now.Ticks; // Static constructor is called at most one time, before any // instance constructor is invoked or member is accessed. //static SimpleClass() //{ //} } 

?


This does not deceive another question, it concerns static constructors that do not accept parameters.

+5
source share
2 answers

The need is somehow obvious: you want to do more than initialize the field for your static members.

Logically, if you have this class:

 class SimpleClass { // Static variable that must be initialized at run time. static readonly long baseline = DateTime.Now.Ticks; } 

You can rewrite it to have the same effect:

 class SimpleClass { // Static variable that must be initialized at run time. static readonly long baseline; static SimpleClass () { baseline = DateTime.Now.Ticks; } } 

But instead, you could do more in your static constructor, for example, check (using reflection) some properties and give them some quick accessors / getters or just just notify other systems that created your type, etc.

From Jeffrey Richter CLR via C # book:

When the C # compiler sees a class with static fields that use inline initialization (the BeforeFieldInit class), the compiler emits a record of the class type definition table with metadata BeforeFieldInit flag. When the C # compiler sees a class with an explicit constructor type (Precise class), the compiler emits a class type record table definition without the metadata flag BeforeFieldInit. The rationale for this is as follows: the initialization of static fields must be performed before accessing the fields, while the type constructor may contain arbitrary code that may have observed side effects; this code may be required to execute at a specific time.

Obviously there is more to this than going on behind the scenes; I would suggest you read an entire chapter from the CLR through C #: "Type Constructors"

+5
source

This is an example of a possible difference:

 class SimpleClass { static readonly long A = DateTime.Now.Ticks; static readonly long B = DateTime.Now.Ticks; static SimpleClass() { } } 

A and B not guaranteed the same value, although if you were to write it in the constructor, you could guarantee it:

 class SimpleClass { static readonly long A; static readonly long B; static SimpleClass() { var ticks = DateTime.Now.Ticks; A = ticks; B = ticks; } } 

In addition, order matters for an instance of static elements.

According to ECMA-334 regarding static field initialization:

The initializers of the static variable of the class declaration field correspond to the sequence of assignments that are executed in the text order in which they are displayed in the class declaration. If a static constructor (ยง17.11) exists in the class, the execution of the initializers of the static field occur immediately before the execution of this static constructor. Otherwise, the initializers of the static field executed in the implementation-dependent time until the first use of the static field of this class

So we can write something like this:

 class SimpleClass { public static readonly long A = IdentityHelper.GetNext(); public static readonly long B = IdentityHelper.GetNext(); static SimpleClass() { } } public static class IdentityHelper { public static int previousIdentity = 0; public static int GetNext() { return ++previousIdentity; } } 

Here A guaranteed to be assigned to B In this example, A will be 1 and B will be 2 . We can guarantee that A < B (provided that the identifier is not overflowing and there are no problems with stream processing). Now, if we reorder the fields:

 public static readonly long B = IdentityHelper.GetNext(); public static readonly long A = IdentityHelper.GetNext(); 

Functionality is changing. Thus, we created a side effect that is not immediately cleared simply by redefining the field definitions.

A more likely scenario, we can do this:

 class SimpleClass { public static readonly long A = IdentityHelper.GetExpensiveResult().A; public static readonly long B = IdentityHelper.GetExpensiveResult().B; static SimpleClass() { } } 

Here we cannot share GetExpensiveResult() between fields.

+3
source

Source: https://habr.com/ru/post/1241470/


All Articles