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:进入子类构造函数
2:Sub成员变量的内存被分配
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:进入子类构造函数
2:Sub成员变量的内存被分配
3:为Sub成员变量赋值
4:调用父类构造函数
5:调用子类的方法SetValue(子类覆写了这个方法),value被赋值
6:正式执行子类构造函数,成员变量value再次被赋值
同意以上观点的人请放过我,别吐槽,不同意的请留言
这样的解释答案就很合理,但同时也说明成员变量在定义的时候初始化和在构造函数中赋值的意义是不一样的,至少执行顺序不一样,产生的结果可能也不一样。
作者:陈太汉
博客:http://www.cnblogs.com/hlxs/