您的位置:首页 > 编程语言 > C#

C# 小测试(一):类成员初始化与构造函数执行的顺序

2008-07-12 20:25 288 查看
看看下面这段代码,你觉得它会输出什么呢?

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字段是在构造函数中进行的,那么上面的确保机制就不复存在了。

参考:

WhyDoInitializersRunInTheOppositeOrderAsConstructors?PartOne

WhyDoInitializersRunInTheOppositeOrderAsConstructors?PartTwo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐