对beforefieldinit的理解
今天看了Artech,
关于Type Initializer和 BeforeFieldInit的问题,看看大家能否给出正确的解释
http://www.cnblogs.com/artech/archive/2008/11/01/1324280.html
,的文章,并看了Artech,TerryLee,Anytao几位高人的讨论,对NET的理解又加深不少,心情不错,也写一篇对于beforefieldinit理解的文章
原文中的一段例子代码
using System; namespace Artech.TypeInitializerDemo { class Program { static void Main() { Console.WriteLine("Start ..."); Foo.GetString("Manually invoke the static GetString() method!"); string field = Foo.Field; } } class Foo { public static string Field = GetString("Initialize the static field!"); public static string GetString(string s) { Console.WriteLine(s); return s; } } } |
出现这种情况的原因是beforefieldinit关键字的使用.
下面是地两段用IL实现上面C#的例子的代码,匹别只是一段使用了beforefieldinit,一段没使用
例1:用IL实现上面C#的例子,使用beforefieldinit
这是上面C#的例子的默认实现方式
.assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) .ver 2:0:0:0 }
.assembly myTest { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .hash algorithm 0x00008004 .ver 0:0:0:0 }
.module myTest.dll .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 .corflags 0x00000001
.namespace myTest { .class public auto ansi beforefieldinit Test1 extends [mscorlib]System.Object { .method private hidebysig specialname rtspecialname static void .cctor() cil managed { .maxstack 8 L_0000: ldstr "Initialize the static field!" L_0005: call string myTest.Test1::GetString(string) L_000a: stsfld string myTest.Test1::Field L_000f: ret }
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: call instance void [mscorlib]System.Object::.ctor() L_0006: ret }
.method public hidebysig static string GetString(string s) cil managed { .maxstack 1 .locals init ( [0] string CS$1$0000) L_0000: nop L_0001: ldarg.0 L_0002: call void [mscorlib]System.Console::WriteLine(string) L_0007: nop L_0008: ldarg.0 L_0009: stloc.0 L_000a: br.s L_000c L_000c: ldloc.0 L_000d: ret }
.field public static string Field
}
} |
编译 ilasm c:\myTest.txt /dll |
在C#中引用上面编译好的Dll class Program { static void Main(string[] args) { Console.WriteLine("Start ..."); myTest.Test1.GetString("Manually invoke the static GetString() method!"); string field = myTest.Test1.Field; Console.Read(); } } |
运行结果与上面C#的例子一样 |
在VS加个断点,可以看到代码还没运行到[ string field = myTest.Test1.Field;] ,但[myTest.Test1.Field]已经有值了 |
例2:用IL实现上面C#的例子,不使用beforefieldinit
.assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) .ver 2:0:0:0 }
.assembly myTest { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .hash algorithm 0x00008004 .ver 0:0:0:0 }
.module myTest.dll .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 .corflags 0x00000001
.namespace myTest { .class public auto ansi Test1 extends [mscorlib]System.Object { .method private hidebysig specialname rtspecialname static void .cctor() cil managed { .maxstack 8 L_0000: ldstr "Initialize the static field!" L_0005: call string myTest.Test1::GetString(string) L_000a: stsfld string myTest.Test1::Field L_000f: ret }
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: call instance void [mscorlib]System.Object::.ctor() L_0006: ret }
.method public hidebysig static string GetString(string s) cil managed { .maxstack 1 .locals init ( [0] string CS$1$0000) L_0000: nop L_0001: ldarg.0 L_0002: call void [mscorlib]System.Console::WriteLine(string) L_0007: nop L_0008: ldarg.0 L_0009: stloc.0 L_000a: br.s L_000c L_000c: ldloc.0 L_000d: ret }
.field public static string Field
}
} |
编译 ilasm c:\myTest.txt /dll |
在C#中引用上面编译好的Dll class Program { static void Main(string[] args) { Console.WriteLine("Start ..."); myTest.Test1.GetString("Manually invoke the static GetString() method!"); string field = myTest.Test1.Field; Console.Read(); } } |
这个结果是与通常的预期一致 |
同样在VS加个断点,可以看到[myTest.Test1.Field]无值 |
beforefieldinit到底要做什么
先分析一下[例1]的现象,Test1是何时被构造的,静态构造会在类第一次访问时进行,但是[例1]的第一次访问是在[ string field = myTest.Test1.Field;]中,可是构造为何会发生在[ Console.WriteLine("Start ...");]之前
下面是我分析出的结论,方法在加载前会先将方法体内所有变量筛选一便,如果发现有特殊标记(如beforefieldinit),会将该变量提前放到一个缓存中,也就是说Main方法在初始化时,就已经去访问[Field]了,
还有,我觉得field不在方法体的栈中,有时我感NET中没有栈,或者说参与闭包的私有变量不在栈中