C#成員初始化有點坑爹


C#成員的初始化順序你真的非常清楚嗎,我發現有點坑爹,坑到爹突然有點搞不清楚什么狀況。下面咱們開始分析,先看3個簡單類。

 

    public abstract class Base
    {
        public Base()
        {
            SetValue();
        }
        public abstract void SetValue();
    }

    public class Sub : Base
    {
        public string value;
        public Sub()
        {
            value = "chentaihan";
        }

        public override void SetValue()
        {
            value = "陳太漢";
        }
    }

    public class Sub1 : Base
    {
        public string value = "chentaihan";

        public override void SetValue()
        {
            value = "陳太漢";
        }
    }

 

如果執行下面這段代碼會輸出什么值呢,請不要往下看,先給出你自己的答案。

 

 static class Program
    {
        static void Main(string[] args)
        {
            Sub sub = new Sub();
            Console.WriteLine(sub.value);
            Sub1 sub1 = new Sub1();
            Console.WriteLine(sub1.value);
            Console.Read();
        }
    }

 

是的他很簡單,但你確信你的答案就是對的嗎?這么一個簡單的問題我答錯了,所以就有了這篇博客。 CLR VIA C#這本書告訴我們:成員在定義的時候初始化相當於在構造函數的最上面初始化,如果一個成員在定義的時候初始化,並在構造函數中賦值,那么在構造函數執行完成之后,該成員的值就是造函數中所賦的值,所以我得出的答案都是:chentaihan。但答案不是這樣的。當運行結果出來時,我那個迷茫啊.....

 

先來說說我的簡單分析:

1:進入子類構造函數

2Sub成員變量的內存被分配

3:調用父類構造函數

4:調用子類的方法SetValue(子類覆寫了這個方法)value被賦值

5:正式執行子類構造函數,成員變量value再次被賦值

從上面5步我得出他們輸出的結果一樣,都是chentaihan。錯在哪里呢?

於是我用Reflector查看了一下,得到的結果正如上面所說,他們的源碼是一樣的,如下所示。正如CLR VIA C#這本書說的那樣,那為什么結果不一樣呢,Reflector代碼是一樣的,執行的結果卻不一樣,怎么回事,怎么回事,那我只能說Reflector坑爹,它不能反映程序的真正執行邏輯,非要我用IL,我用的還不熟呢。

   public class Sub : Base
    {
        public string value;
        public Sub()
        {
            value = "chentaihan";
        }

        public override void SetValue()
        {
            value = "陳太漢";
        }
    }

 

神馬情況,他們的IL代碼是不一樣的,如圖所示

看了這個圖,我們知道答案是chentaihan,陳太漢。誰能告訴我怎么調用父類的構造函數和給value賦值的順序不一樣啊。該用的工具都用了,我該怎么證明這個結果,於是開始單步調試,於是發現了一個每天都發現了的秘密:成員初始化在構造函數之前執行。難怪這本書上說成員在定義的時候初始化相當於在構造函數的最上面初始化,Reflector也證實了這個答案。但是又繞進另一個坑爹的問題:構造函數還沒有調用,內存還沒有分配,怎么給成員變量賦值啊?這不是問題,從上圖可以看出成員變量的賦值只是在父類的構造函數之前調用,肯定也是在子類的成員變量分配空間之后為成員變量賦值。好的,最后我們得出的結論是: 

1:進入子類構造函數

2Sub成員變量的內存被分配

3Sub成員變量賦值

4:調用父類構造函數

5:調用子類的方法SetValue(子類覆寫了這個方法)value被賦值

6:正式執行子類構造函數,成員變量value再次被賦值

同意以上觀點的人請放過我,別吐槽,不同意的請留言

這樣的解釋答案就很合理,但同時也說明成員變量在定義的時候初始化和在構造函數中賦值的意義是不一樣的,至少執行順序不一樣,產生的結果可能也不一樣。

 

 

 

作者:陳太漢

博客:http://www.cnblogs.com/hlxs/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM