Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   Type Constructors

In addition to instance constructors, the CLR supports type constructors (also known as static con- structors, class constructors, or type initializers). A type constructor can be applied to interfaces (although C# doesn’t allow this), reference types, and value types. Just as instance constructors are used to set the initial state of an instance of a type, type constructors are used to set the initial state of a type. By default, types don’t have a type constructor defined within them. If a type has a type


constructor, it can have no more than one. In addition, type constructors never have parameters. In C#, here’s how to define a reference type and a value type that have type constructors.

 

internal sealed class SomeRefType { static SomeRefType() {

// This executes the first time a SomeRefType is accessed.

}

}

 

internal struct SomeValType {

// C# does allow value types to define parameterless type constructors. static SomeValType() {

// This executes the first time a SomeValType is accessed.

}

}

 

You’ll notice that you define type constructors just as you would parameterless instance construc- tors, except that you must mark them as static. Also, type constructors should always be private; C# makes them private for you automatically. In fact, if you explicitly mark a type constructor as private (or anything else) in your source code, the C# compiler issues the following error: error

CS0515: 'SomeValType.SomeValType()': access modifiers are not allowed on static constructors. Type constructors should be private to prevent any developer-written code from calling them; the CLR is always capable of calling a type constructor.

       
   
 

 

The calling of a type constructor is a tricky thing. When the just-in-time (JIT) compiler is compiling a method, it sees what types are referenced in the code. If any of the types define a type constructor, the JIT compiler checks if the type’s type constructor has already been executed for this AppDomain. If the constructor has never executed, the JIT compiler emits a call to the type constructor into the native code that the JIT compiler is emitting. If the type constructor for the type has already executed, the JIT compiler does not emit the call because it knows that the type is already initialized.


Now, after the method has been JIT-compiled, the thread starts to execute it and will eventu- ally get to the code that calls the type constructor. In fact, it is possible that multiple threads will be

executing the same method concurrently. The CLR wants to ensure that a type’s constructor executes only once per AppDomain. To guarantee this, when a type constructor is called, the calling thread acquires a mutually exclusive thread synchronization lock. So if multiple threads attempt to simulta- neously call a type’s static constructor, only one thread will acquire the lock and the other threads will block. The first thread will execute the code in the static constructor. After the first thread leaves the constructor, the waiting threads will wake up and will see that the constructor’s code has already been executed. These threads will not execute the code again; they will simply return from the construc- tor method. In addition, if any of these methods ever get called again, the CLR knows that the type constructor has already executed and will ensure that the constructor is not called again.



       
   
 
 

 

Within a single thread, there is a potential problem that can occur if two type constructors contain code that reference each other. For example, ClassA has a type constructor containing code that references ClassB, and ClassB has a type constructor containing code that references ClassA. In this situation, the CLR still guarantees that each type constructor’s code executes only once; however, it cannot guarantee that ClassA’s type constructor code has run to completion before executing ClassB’s type constructor. You should certainly try to avoid writing code that sets up this scenario. In fact, because the CLR is responsible for calling type constructors, you should always avoid writing any code that requires type constructors to be called in a specific order.

Finally, if a type constructor throws an unhandled exception, the CLR considers the type to be unusable. Attempting to access any fields or methods of the type will cause a System.Type­ InitializationException to be thrown.

The code in a type constructor has access only to a type’s static fields, and its usual purpose is to initialize those fields. As it does with instance fields, C# offers a simple syntax that allows you to initialize a type’s static fields.

 

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

}

       
   
 
 


When this code is built, the compiler automatically generates a type constructor for SomeType. It’s as if the source code had originally been written as follows.

 

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

}

 

Using ILDasm.exe, it’s easy to verify what the compiler actually produced by examining the IL for the type constructor. Type constructor methods are always called .cctor (for class constructor) in a method definition metadata table.

In the code below, you see that the .cctor method is private and static. In addition, notice that the code in the method does in fact load a 5 into the static field s_x.

 

.method private hidebysig specialname rtspecialname static void .cctor() cil managed

{

// Code size 7 (0x7)

.maxstack 8 IL_0000: ldc.i4.5

IL_0001: stsfld int32 SomeType::s_x IL_0006: ret

} // end of method SomeType::.cctor

 

Type constructors shouldn’t call a base type’s type constructor. Such a call isn’t necessary because

none of a type’s static fields are shared or inherited from its base type.

       
   
 
 

 

Finally, assume that you have this code.

 

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

 

static SomeType() { s_x = 10;

}

}


In this case, the C# compiler generates a single type constructor method. This constructor first initializes s_x to 5 and then initializes s_x to 10. In other words, when the C# compiler generates IL code for the type constructor, it first emits the code required to initialize the static fields followed by the explicit code contained in your type constructor method.

       
   
 
 

 

 


Date: 2016-03-03; view: 695


<== previous page | next page ==>
Nbsp;   Fields | Nbsp;   Operator Overload Methods
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.008 sec.)