Programming Life

.NET World

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

在本节中,Richter提到类型构造器的两种语义:precise和before-field-init。对C#编译器而言,如果发现有显式的类型构造器,它就看成precise语义;反之,则当成before-field-init对待。

internal sealed class Precise
{
    
public static Int32 s_x;
    
static Precise() { s_x = 123; } // precise语义,这是显式的类型构造器
}

IL代码如下: 

.class private auto ansi sealed Precise

       extends [mscorlib]System.Object

{

 .field 
public static int32 s_x

 .method 
private hidebysig specialname rtspecialname static 

          
void .cctor() cil managed

 
{

    
// Code size       9 (0x9)

    .maxstack 
8

    IL_0000: nop

    IL_0001: ldc.i4.s   
123

    IL_0003: stsfld     int32 Precise::s_x

    IL_0008: ret

 }
 // end of method Precise::.cctor

 .method 
public hidebysig specialname rtspecialname 

          instance 
void .ctor() cil managed

 
{

    
// Code size       7 (0x7)

    .maxstack 
8

    IL_0000: ldarg.
0

    IL_0001: call       instance 
void [mscorlib]System.Object::.ctor()

    IL_0006: ret

 }
 // end of method Precise::.ctor

}
 // end of class Precise



internal sealed class BeforeFieldInit

{

public static Int32 s_x = 123// before-field-init语义,这里仅仅是一个静态字段,

// C#编译器会在IL中构造一个类型构造器,但对这个类会多加上一个修饰以关键字beforefieldinit表示

}



IL代码如下: 

.class private auto ansi sealed beforefieldinit BeforeFieldInit

       extends [mscorlib]System.Object

{

 .field 
public static int32 s_x

 .method 
public hidebysig specialname rtspecialname 

          instance 
void .ctor() cil managed

 
{

    
// Code size       7 (0x7)

    .maxstack 
8

    IL_0000: ldarg.
0

    IL_0001: call       instance 
void [mscorlib]System.Object::.ctor()

    IL_0006: ret

 }
 // end of method BeforeFieldInit::.ctor

 .method 
private hidebysig specialname rtspecialname static 

          
void .cctor() cil managed

 
{

    
// Code size       8 (0x8)

    .maxstack 
8

    IL_0000: ldc.i4.s   
123

    IL_0002: stsfld     int32 BeforeFieldInit::s_x

    IL_0007: ret

 }
 // end of method BeforeFieldInit::.cctor

}
 // end of class BeforeFieldInit

 类型构造器的调用时机是在第一次存取静态成员或第一次创建实例之前,总之是确保在存取静态成员或创建实例的时候类型是初始化已经完成。因此,如果在静态构造器中抛出异常,则在创建该类型的实例时会catch异常TypeInitializationException。 如下面的例子:


 

class TestClass1

{

    
public static int m_y;

    
static TestClass1()

    
{

        
throw new Exception( "Exception from cctor" );

        Console.WriteLine( 
"TestClass1::cctor" );

    }


    
public Int32 m_x;

}


// 使用TestClass1

TestClass1 test 
=null;

try

{

    test 
= new TestClass1();

}


catch ( TypeInitializationException ex )

{

    Console.WriteLine( ex.ToString() );

}


// If type is not initialized successfully, the object is null

test.m_x 
= 1;




before-field-init语义其实是一种“松散(Relaxed)”语义,就是说CLR可以在先于存取静态成员的任一时候得到调用。而precise语义则是在刚好调用存取静态成员之前使静态构造函数得到调用。但有一点是一致的:用户不能显式调用静态构造函数,因为从IL代码可能以看出,静态构造函数是私有的,这就阻止了用户的显式调用。

before-field-init语义的性能好是因为在你准备使用某个类之前的某个时候,JIT已经编译过了;而precise语义则是相对在使用某个类之前的某个很短的时间内调用。这样看来书上的例子也验证了before-field-init的静态构造函数的调用要“快”于precise语义。我写了一个测试程序:

 

.assembly extern mscorlib

{

}


.assembly test

{

 }


.module test.exe

// MVID: {E0B4B628-AEE0-4456-8EFB-14A10EEBCBE0}

.imagebase 
0x00400000

.file alignment 
0x00000200

.stackreserve 
0x00100000

.subsystem 
0x0003       // WINDOWS_CUI

.corflags 
0x00000001    // ILONLY

// Image base: 0x02F50000

// =============== CLASS MEMBERS DECLARATION ===================

.
class private auto ansi sealed beforefieldinit BeforeFieldInit

       extends [mscorlib]System.Object

{

 .field 
public static int32 s_x

 .method 
public hidebysig specialname rtspecialname 

          instance 
void .ctor() cil managed

 
{

    
// Code size       7 (0x7)

    .maxstack 
8

    IL_0000: ldarg.
0

    IL_0001: call       instance 
void [mscorlib]System.Object::.ctor()

    IL_0006: ret

 }
 // end of method BeforeFieldInit::.ctor

 .method 
private hidebysig specialname rtspecialname static 

          
void .cctor() cil managed

 
{

    
// Code size       8 (0x8)

    .maxstack 
8

    ldstr 
"BeforeFieldInit::cctor"

    call 
void [mscorlib]System.Console::WriteLine(string)

    IL_0000: ldc.i4.s   
123

    IL_0002: stsfld     int32 BeforeFieldInit::s_x

    IL_0007: ret

 }
 // end of method BeforeFieldInit::.cctor

}
 // end of class BeforeFieldInit

.
class private auto ansi sealed Precise

       extends [mscorlib]System.Object

{

 .field 
public static int32 s_x

 .method 
private hidebysig specialname rtspecialname static 

          
void .cctor() cil managed

 
{

    
// Code size       9 (0x9)

    .maxstack 
8

    ldstr 
"Precise::cctor"

    call 
void [mscorlib]System.Console::WriteLine(string)

    IL_0000: nop

    IL_0001: ldc.i4.s   
123

    IL_0003: stsfld     int32 Precise::s_x

    IL_0008: ret

 }
 // end of method Precise::.cctor

 .method 
public hidebysig specialname rtspecialname 

          instance 
void .ctor() cil managed

 
{

    
// Code size       7 (0x7)

    .maxstack 
8

    IL_0000: ldarg.
0

    IL_0001: call       instance 
void [mscorlib]System.Object::.ctor()

    IL_0006: ret

 }
 // end of method Precise::.ctor

}
 // end of class Precise

.
class public auto ansi sealed beforefieldinit Program

       extends [mscorlib]System.Object

{

 .method 
public hidebysig static void Main() cil managed

 
{

    .entrypoint

    
// Code size       20 (0x14)

    .maxstack 
1

    .locals init (
class Precise V_0,

             
class BeforeFieldInit V_1)

    IL_0000: nop

    IL_0001: call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()

    IL_0006: pop

    IL_0007: newobj     instance 
void Precise::.ctor()

    IL_000c: stloc.
0

    IL_000d: newobj     instance 
void BeforeFieldInit::.ctor()

    IL_0012: stloc.
1

    IL_0013: ret

 }
 // end of method Program::Main

 .method 
public hidebysig specialname rtspecialname 

          instance 
void .ctor() cil managed

 
{

    
// Code size       7 (0x7)

    .maxstack 
8

    IL_0000: ldarg.
0

    IL_0001: call       instance 
void [mscorlib]System.Object::.ctor()

    IL_0006: ret

 }
 // end of method Program::.ctor

}
 // end of class Program


用ILASM test.il编译

运行编译后的test.exe

程序首先进入System.Console::ReadKey的调用,此时我们还没有操作BeforeFieldInit或Precise的成员,但你已经发现BeforeFieldInit::cctor已经得到调用了。而只有在我们键入键程序继续运行后,Precise::cctor才得以调用。

Before-field-init的另一点需要注意的是多个Before-field-init语义的构造函数存在时,CLR不保证它们调用的先后顺序。因此如果这些静态构造函数内部实现有关联的话,它可能不会按照你声明的顺序调用。因此最后我们不要写类似的代码。

posted on 2007-09-13 01:40  许文科  阅读(251)  评论(0编辑  收藏  举报