`
varsoft
  • 浏览: 2508718 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

[原创] 关于Type Initializer和 BeforeFieldInit的问题,看看大家能否给出正确的解释

 
阅读更多

下面通过一个简单的Console Application演示Type Innitializer的执行顺序。希望大家各抒己见,对于实验的结果给出一个圆满的解释,同时希望读者从中理解到更多关于编译、关于CLR一些被我们忽略的细节。

代码如下,在类Foo中定义了两个static成员:静态字段Field和静态方法GetString,Field通过于Inline的方式通过调用GetString进行初始化。在Main()中仅仅两行代码:Console.WriteLine("Start ...");Foo.GetString("Manually invoke the static GetString() method!");

   1: using System;
<!--CRLF-->
   2: namespace Artech.TypeInitializerDemo
<!--CRLF-->
   3: {
<!--CRLF-->
   4:     class Program
<!--CRLF-->
   5:     {
<!--CRLF-->
   6:         static void Main()
<!--CRLF-->
   7:         {
<!--CRLF-->
   8:             Console.WriteLine("Start ...");
<!--CRLF-->
   9:             Foo.GetString("Manually invoke the static GetString() method!");
<!--CRLF-->
  10:         }
<!--CRLF-->
  11:     }
<!--CRLF-->
  12: 
<!--CRLF-->
  13:     class Foo
<!--CRLF-->
  14:     {
<!--CRLF-->
  15:         public static string Field = GetString("Initialize the static field!");
<!--CRLF-->
  16: 
<!--CRLF-->
  17:         public static string GetString(string s)
<!--CRLF-->
  18:         {
<!--CRLF-->
  19:             Console.WriteLine(s);
<!--CRLF-->
  20:             return s;
<!--CRLF-->
  21:         }
<!--CRLF-->
  22:     }
<!--CRLF-->
  23: } 
<!--CRLF-->

对于结果,我想很多人都能够猜得到,如果在显示调用GetString()之前,需要完成静态成员的初始化,所以最终的输出结果如下图所示:

image

然后我们在Main()种多加一行代码:string field = Foo.Field; 也就是获取Foo的静态字段:

   1: static void Main()
<!--CRLF-->
   2: {
<!--CRLF-->
   3:     Console.WriteLine("Start ...");
<!--CRLF-->
   4:     Foo.GetString("Manually invoke the static GetString() method!");
<!--CRLF-->
   5:     string field = Foo.Field;            
<!--CRLF-->
   6: }
<!--CRLF-->

最终的输出结果就和上面不一样了,静态字段的初始化工作居然提前了(在Console.WriteLine("Start ...");之前执行)

image

“神奇”的事情还没有结束,如果我们在Foo中加上一个静态构造函数,其中不执行任何的操作:

   1: class Foo
<!--CRLF-->
   2: {
<!--CRLF-->
   3:     public static string Field = GetString("Initialize the static field!");
<!--CRLF-->
   4: 
<!--CRLF-->
   5:     static Foo()
<!--CRLF-->
   6:     { }
<!--CRLF-->
   7: 
<!--CRLF-->
   8:     public static string GetString(string s)
<!--CRLF-->
   9:     {
<!--CRLF-->
  10:         Console.WriteLine(s);
<!--CRLF-->
  11:         return s;
<!--CRLF-->
  12:     }
<!--CRLF-->
  13: }
<!--CRLF-->

再来看看现在执行结果,又和先前的一样的了。

image
我先不做任何评论(因为我也不太确定我的认识就是正确的),看看大家对此有什么看法。

再添加另一个static constructor的例子,较之上面一个要简单点。在Bar继承自基类Foo,在Foo和Bar均定义了静态构造函数。静态方法DoSomething()定义在Foo中,在Main()中却通过Bar.DoSomething();进行调用。

   1: using System;
<!--CRLF-->
   2: namespace Artech.TypeInitializerDemo
<!--CRLF-->
   3: {
<!--CRLF-->
   4:     class Program
<!--CRLF-->
   5:     {
<!--CRLF-->
   6:         static void Main()
<!--CRLF-->
   7:         {
<!--CRLF-->
   8:             Bar.DoSomething();
<!--CRLF-->
   9:         }
<!--CRLF-->
  10:     }
<!--CRLF-->
  11: 
<!--CRLF-->
  12:     public abstract class Foo
<!--CRLF-->
  13:     {
<!--CRLF-->
  14:         static Foo()
<!--CRLF-->
  15:         {
<!--CRLF-->
  16:             Console.WriteLine("static Foo() is invoked");
<!--CRLF-->
  17:         }
<!--CRLF-->
  18: 
<!--CRLF-->
  19:         public static void DoSomething()
<!--CRLF-->
  20:         {
<!--CRLF-->
  21:             Console.WriteLine("Done ...");
<!--CRLF-->
  22:         }
<!--CRLF-->
  23:     }
<!--CRLF-->
  24: 
<!--CRLF-->
  25:     public class Bar : Foo
<!--CRLF-->
  26:     {
<!--CRLF-->
  27:         static Bar()
<!--CRLF-->
  28:         {
<!--CRLF-->
  29:             Console.WriteLine("static Bar() is invoked");
<!--CRLF-->
  30:         }
<!--CRLF-->
  31:     }
<!--CRLF-->
  32: } 
<!--CRLF-->

下面是输出结果,可见虽然通过Bar调用了静态方法DoSomething,但是Bar的静态构造函数没有被执行。这个很好理解,因为Something是定义在基类Foo上,Bar.DoSomething()本质上相当于Foo.DoSomething()。所以只会调用Foo的静态构造函数。

image

个人觉得,这是编译器值得改进的地方,既然静态方法是基于类型的方法,只能通过定义了该静态方法的那个类型进行调用,至于其他的类,哪怕是该类的子类,都不能调用该方法。编译器不应该让这样的代码通过编译。不知道读者的意见如何。


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics