原文地址:http://www.csharp411.com/c-object-initialization/
译者:star65225692
当构造一个C#对象时,理解对象的作用域和构造器被初始化的序列是很重要的
- Derived static fields
- Derived static constructor
- Derived instance fields
- Base static fields
- Base static constructor
- Base instance fields
- Base instance constructor
- Derived instance constructor
示例
下面的是一个C#控制台程序的例子,这个例子演示了对象初始化的次序这个程序创建基对象的派生对象,包含了静态化和实例化构造器以及作用域,两个作用域"Field1"和"Field2"在他们的定义里被初始化,然而 "Field3"在构造器里被初始化.同时也包含了一个证明为什么不能从构造器里调用虚方法的虚方法.当每个作用域和构造器被初始化时就会写入控制台,因此你可以看到初始化的序列
using System; namespace ObjectInit { class Program { static void Main( string[] args ) { Derived d = new Derived(); Console.ReadLine(); } } class Base { public Base() { Console.WriteLine( "Base.Instance.Constructor" ); this.m_Field3 = new Tracker( "Base.Instance.Field3″ ); this.Virtual(); } static Base() { Console.WriteLine( "Base.Static.Constructor" ); } private Tracker m_Field1 = new Tracker( "Base.Instance.Field1″ ); private Tracker m_Field2 = new Tracker( "Base.Instance.Field2″ ); private Tracker m_Field3; static private Tracker s_Field1 = new Tracker( "Base.Static.Field1″ ); static private Tracker s_Field2 = new Tracker( "Base.Static.Field2″ ); virtual public void Virtual() { Console.WriteLine( "Base.Instance.Virtual" ); } } class Derived : Base { public Derived() { Console.WriteLine( "Derived.Instance.Constructor" ); this.m_Field3 = new Tracker( "Derived.Instance.Field3″ ); } static Derived() { Console.WriteLine( "Derived.Static.Constructor" ); } private Tracker m_Field1 = new Tracker( "Derived.Instance.Field1″ ); private Tracker m_Field2 = new Tracker( "Derived.Instance.Field2″ ); private Tracker m_Field3; static private Tracker s_Field1 = new Tracker( "Derived.Static.Field1″ ); static private Tracker s_Field2 = new Tracker( "Derived.Static.Field2″ ); override public void Virtual() { Console.WriteLine( "Derived.Instance.Virtual" ); } } class Tracker { public Tracker( string text ) { Console.WriteLine( text ); } } }
下面就是来自这个示例程序的控制台输出
Derived.Static.Field1
Derived.Static.Field2
Derived.Static.Constructor
Derived.Instance.Field1
Derived.Instance.Field2
Base.Static.Field1
Base.Static.Field2
Base.Static.Constructor
Base.Instance.Field1
Base.Instance.Field2
Base.Instance.Constructor
Base.Instance.Field3
Derived.Instance.Virtual
Derived.Instance.Constructor
Derived.Instance.Field3
2个阶段的构建
注意在C#对象初始化的问题中还有个潜在的圈套。当初始化以派生对象开始转到基类对象时,构造函数以相反次序进行初始化,先执行基类构造函数,再执行派生函数。
当你试图通过基构造函数调用一个虚方法时,上述情况就会发生。因为派生构造函数还未被执行,所以当虚方法通过基类函数执行时,派生对象或许就不会完全初始化。
正如在案例程序显示的那样,当基构造函数调用虚方法时(程序输出显示:Derived.Instance.Virtual),由于派生构造函数未被执行,派生对象的Field3并未被初始化。如果虚方法是建立在一个需要被初始化的Field3,此时程序将出错。
因此,你不能在对象函数中使用虚方法。而是在你第一次构建对象处执行“2个阶段的构建”,此时再使用虚方法,如下:
Derived d = new Derived();
d.Virtual();
C#对象初始化提示:
一些关于C#对象初始化的一般规则和提示:
先变量后构造函数.变量先被初始化,然后构造函数被执行
先静态化后实例化.当一个类被访问时,静态变量和构造函数最先被初始化.接着是对象的实例化变量和构造函数被初始化
先派生类后基类.对于变量和静态构造函数,派生对象在基对象之前被初始化.比如C类派生自B类,B类派生自A类,那么变量和静态构造函数被初始化次序是C-B-A.
除了实例构造函数. 对于实例构造函数,基类构造函数在派生类构造函数之前执行,实例构造函数被执行次序是A-B-C.
不要假定变量的次序.Fields依据它们在源文件中的声明的顺序依次初始化.然而,自从程序员和工具可以随意安排变量的声明后,你不应该在依靠变量任何特别的次序初始化
对虚方法用两个阶段的构建.避免从一个构造器调用虚方法. 如果在初始化一个对象时需要调用一些虚方法,应在完整构造该对象的地方使用两阶段的构建,并随后调用已构造对象的初始化方法。