看起来这是一个不太容易不被注意的一个问题,
using System;
class Foo
{
public Foo(string s)
{
Console.WriteLine("Foo constructor: {0}", s);
}
public void Bar() { }
}
class Base
{
readonly Foo baseFoo = new Foo("Base initializer");
public Base()
{
Console.WriteLine("Base constructor");
}
}
class Derived : Base
{
readonly Foo derivedFoo = new Foo("Derived initializer");
public Derived()
{
Console.WriteLine("Derived constructor");
}
}
static class Program
{
static void Main()
{
new Derived();
}
}
这段代码的实际输出是:
Foo constructor: Derived initializer
Foo constructor: Base initializer
Base constructor
Derived constructor
我觉得关键的注意点是,C#类需要确保类型(Type)正确性(避免有NULL),所以总是在一个类正式初始化之前进行成员的初始化,接下来的动作依次类推并递归。下面是比较合理的解释,
Process of creating an Instance of a Class goes in the following order: (static would be different)
Step 1) Compiler executes code related to instance variable declarations; this is to identify the fields required to construct the instance.
Step 2) Compiler executes the desired constructor - Constructor is the first method that is called on an instance.
Trick is here: To construct a Derived class instance a base class instance needs to be constructed.
Let’s get back to the code we are looking at:
1) Line 'new Derived();' of the 'static void Main()' method creates an instance of 'Derived' class. So the compiler should run Step 1 on 'Derived' class.
Result: 'Foo constructor: Derived initializer' is written to the console.
2) Compiler should run Step 2 on 'Derived' class. To create an instance of the derived class it first needs to construct the base class instance. Hence,
i) Step 1 is called on 'Base' class.
Result: 'Foo constructor: Base initializer' is written to the console.
ii) After Step 1, Step 2 is called on 'Base' class.
Result: 'Base constructor' is written to the console.
Since the Base class instance is constructed, it finishes with constructing the Derived class by calling Step 2 on the 'Derived' class
Result: 'Derived constructor' is written to the console.
而在第二篇( http://blogs.msdn.com/ericlippert/archive/2008/02/18/why-do-initializers-run-in-the-opposite-order-as-constructors-part-two.aspx)中,更是有详细的例子解释说明如果不这样做的问题。(当然它来自不良的变成习惯)。
对此C#初始顺序的解释(我想同样适合于Java),引起了很多C++用户的再次的“笑”评。其实,个中原因还是托管型语言希望拥有更稳定的解释执行环境,强调了“类型安全和检查”的方针执行,所以不能说谁好谁差。
不过,倒是提醒我们在没有特殊的情况下,不要在构造函数外初始化成员变量,也许是一个好的习惯。
原文:Why Do Initializers Run In The Opposite Order As Constructors