C# 小测试(一):类成员初始化与构造函数执行的顺序
2008-07-12 20:25
288 查看
看看下面这段代码,你觉得它会输出什么呢?
[code]{
[/code]
先猜一下吧,似乎应该是“Baseinitializer,Baseconstructor,Derivedinitializer,Derivedconstructor”。
事实上,应当是先执行类成员的初始化,顺序是从derived到base,然后是两个构造函数,顺序是从base从derived。
这种方式是很有意义的,在类继承体系中层次较深的类(离System.Object较远)将依赖于较浅的类(离System.Object较近)。但是很多人会相信调用的顺序应当等价于下面的伪代码:
[code]BaseConstructor()
[/code]
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
而实际情况则是:
[code]BaseConstructor()
[/code]
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
那么,这样处理是为什么呢?
...
...
...
我们来看一下,如果代码按期望的顺序(第一段伪代码)执行,会产生什么问题:
[code]{
[/code]
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
看Base类的构造函数,如果按期望的顺序执行,那么在Base方法执行时,Derived类的实例成员并没有得到初始化,此时就会有NullReference异常了。
而按照实际执行的顺序,所有的实例成员都能确保被完整地初始化:)
当然了,如果readonly字段是在构造函数中进行的,那么上面的确保机制就不复存在了。
参考:
WhyDoInitializersRunInTheOppositeOrderAsConstructors?PartOne
WhyDoInitializersRunInTheOppositeOrderAsConstructors?PartTwo
classFoo
[code]{
publicFoo(strings)
{
Console.WriteLine("Fooconstructor:{0}",s);
}
publicvoidBar(){}
}
classBase
{
readonlyFoobaseFoo=newFoo("Baseinitializer");
publicBase()
{
Console.WriteLine("Baseconstructor");
}
}
classDerived:Base
{
readonlyFooderivedFoo=newFoo("Derivedinitializer.");
publicDerived()
{
Console.WriteLine("Derivedconstructor");
}
}
classProgram
{
staticvoidMain(string[]args)
{
newDerived();
}
}
[/code]
先猜一下吧,似乎应该是“Baseinitializer,Baseconstructor,Derivedinitializer,Derivedconstructor”。
事实上,应当是先执行类成员的初始化,顺序是从derived到base,然后是两个构造函数,顺序是从base从derived。
这种方式是很有意义的,在类继承体系中层次较深的类(离System.Object较远)将依赖于较浅的类(离System.Object较近)。但是很多人会相信调用的顺序应当等价于下面的伪代码:
//期望的顺序
[code]BaseConstructor()
{
ObjectConstructor();
baseFoo=newFoo("Baseinitializer");
Console.WriteLine("Baseconstructor");
}
DerivedConstructor()
{
BaseConstructor();
derivedFoo=newFoo("Derivedinitializer");
Console.WriteLine("Derivedconstructor");
}
[/code]
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
而实际情况则是:
//实际的顺序
[code]BaseConstructor()
{
baseFoo=newFoo("Baseinitializer");
ObjectConstructor();
Console.WriteLine("Baseconstructor");
}
DerivedConstructor()
{
derivedFoo=newFoo("Derivedinitializer");
BaseConstructor();
Console.WriteLine("Derivedconstructor");
}
[/code]
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
那么,这样处理是为什么呢?
...
...
...
我们来看一下,如果代码按期望的顺序(第一段伪代码)执行,会产生什么问题:
classBase
[code]{
publicBase()
{
Console.WriteLine("Baseconstructor");
if(thisisDerived)(thisasDerived).DoIt();
//如果是在创建Derived类的实例,就会遭遇null。
Blah();
//如果是在创建MoreDerived类的实例,就会遭遇null。
}
publicvirtualvoidBlah(){}
}
classDerived:Base
{
readonlyFooderivedFoo=newFoo("Derivedinitializer");
publicDoIt()
{
derivedFoo.Bar();
}
}
classMoreDerived:Derived
{
publicoverridevoidBlah(){DoIt();}
}
[/code]
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
看Base类的构造函数,如果按期望的顺序执行,那么在Base方法执行时,Derived类的实例成员并没有得到初始化,此时就会有NullReference异常了。
而按照实际执行的顺序,所有的实例成员都能确保被完整地初始化:)
当然了,如果readonly字段是在构造函数中进行的,那么上面的确保机制就不复存在了。
参考:
相关文章推荐
- 【转】C#父类与子类的静态成员变量、实例成员变量、构造函数的执行顺序
- 类成员初始化与构造函数执行的顺序 转自:http://www.cnblogs.com/anderslly/archive/2008/07/12/why-do-initializers-run-in-the-opposite-order-as-constructors.html
- C#类成员变量初始化与构造函数执行的顺序
- 构造函数初始化、static代码块初始化、成员变量初始化、以及在子父类中混合的执行顺序实例
- Java初始化顺序总结及其程序执行过程图- 静态变量、静态代码块、成员变量、构造函数
- C#继承中构造函数,成员变量的执行顺序
- [C#]父类与子类的静态成员变量、实例成员变量、构造函数的执行顺序
- [C#]父类与子类的静态成员变量、实例成员变量、构造函数的执行顺序
- Java成员变量初始化和执行顺序
- java变量,初始化快,构造函数的执行顺序
- java变量,初始化快,构造函数的执行顺序
- C#类的继承中,构造函数的执行顺序
- Java继承的初始化与构造函数的执行顺序问题
- 静态变量、静态块、成员变量、构造函数在类实例化时执行顺序
- C++成员变量、构造函数的初始化顺序
- [C++]父类与子类的静态成员变量、实例成员变量、构造函数的执行顺序
- Java类成员变量、普通成员变量、初始化块、构造方法的初始化和执行顺序
- 有父类的子类实例化时,子类与父类的成员变量,构造函数,代码块的执行顺序
- Java的初始化块、静态初始化块、构造函数的执行顺序及用途探究
- C#中基类和派生类的构造函数以及变量的执行顺序整理