關於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;
2: namespace Artech.TypeInitializerDemo
3: {
4: class Program
5: {
6: static void Main()
7: {
8: Console.WriteLine("Start ...");
9: Foo.GetString("Manually invoke the static GetString() method!");
10: }
11: }
12:
13: class Foo
14: {
15: public static string Field = GetString("Initialize the static field!");
16:
17: public static string GetString(string s)
18: {
19: Console.WriteLine(s);
20: return s;
21: }
22: }
23: }
對於結果,我想很多人都能夠猜得到,如果在顯示調用GetString()之前,需要完成靜態成員的初始化,所以最終的輸出結果如下圖所示:
然後我們在Main()種多加一行代碼:string field = Foo.Field; 也就是獲取Foo的靜態字段:
1: static void Main()
2: {
3: Console.WriteLine("Start ...");
4: Foo.GetString("Manually invoke the static GetString() method!");
5: string field = Foo.Field;
6: }
最終的輸出結果就和上麵不一樣了,靜態字段的初始化工作居然提前了(在Console.WriteLine("Start ...");之前執行)
“神奇”的事情還沒有結束,如果我們在Foo中加上一個靜態構造函數,其中不執行任何的操作:
1: class Foo
2: {
3: public static string Field = GetString("Initialize the static field!");
4:
5: static Foo()
6: { }
7:
8: public static string GetString(string s)
9: {
10: Console.WriteLine(s);
11: return s;
12: }
13: }
再來看看現在執行結果,又和先前的一樣的了。
我先不做任何評論(因為我也不太確定我的認識就是正確的),看看大家對此有什麼看法。
再添加另一個static
constructor的例子,較之上麵一個要簡單點。在Bar繼承自基類Foo,在Foo和Bar均定義了靜態構造函數。靜態方法DoSomething()定義在Foo中,在Main()中卻通過Bar.DoSomething();進行調用。
1: using System;
2: namespace Artech.TypeInitializerDemo
3: {
4: class Program
5: {
6: static void Main()
7: {
8: Bar.DoSomething();
9: }
10: }
11:
12: public abstract class Foo
13: {
14: static Foo()
15: {
16: Console.WriteLine("static Foo() is invoked");
17: }
18:
19: public static void DoSomething()
20: {
21: Console.WriteLine("Done ...");
22: }
23: }
24:
25: public class Bar : Foo
26: {
27: static Bar()
28: {
29: Console.WriteLine("static Bar() is invoked");
30: }
31: }
32: }
下麵是輸出結果,可見雖然通過Bar調用了靜態方法DoSomething,但是Bar的靜態構造函數沒有被執行。這個很好理解,因為Something是定義在基類Foo上,Bar.DoSomething()本質上相當於Foo.DoSomething()。所以隻會調用Foo的靜態構造函數。
個人覺得,這是編譯器值得改進的地方,既然靜態方法是基於類型的方法,隻能通過定義了該靜態方法的那個類型進行調用,至於其他的類,哪怕是該類的子類,都不能調用該方法。編譯器不應該讓這樣的代碼通過編譯。不知道讀者的意見如何。
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-30 16:04:22