程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> C#小測試(一):類成員初始化與構造函數執行的順序

C#小測試(一):類成員初始化與構造函數執行的順序

編輯:關於C#

看看下面這段代碼,你覺得它會輸出什麼呢?

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");
    }
  }
  class Program
  {
    static void Main(string[] args)
    {
      new Derived();
    }
  }

先猜一下吧,似乎應該是“Base initializer, Base constructor, Derived initializer, Derived constructor”。

事實上,應當是先執行類成員的初始化,順序是從derived到base,然後是兩個構造函數,順序是從base從derived。

這種方式是很有意義的,在類繼承體系中層次較深的類(離System.Object較遠)將依賴於較淺的類(離System.Object較近)。但是很多人會相信調用的順序應當等價於下面的偽代碼:

// 期望的順序
BaseConstructor()
{
  ObjectConstructor();
  baseFoo = new Foo("Base initializer");
  Console.WriteLine("Base constructor");
}
DerivedConstructor()
{
  BaseConstructor();
  derivedFoo = new Foo("Derived initializer");
  Console.WriteLine("Derived constructor");
}

而實際情況則是:

// 實際的順序
BaseConstructor()
{
  baseFoo = new Foo("Base initializer");
  ObjectConstructor();
  Console.WriteLine("Base constructor");
}
DerivedConstructor()
{
  derivedFoo = new Foo("Derived initializer");
  BaseConstructor();
  Console.WriteLine("Derived constructor");
}

那麼,這樣處理是為什麼呢?

...

...

...

我們來看一下,如果代碼按期望的順序(第一段偽代碼)執行,會產生什麼問題:

class Base
{
  public Base()
  {
    Console.WriteLine("Base constructor");
    if (this is Derived) (this as Derived).DoIt();
    // 如果是在創建Derived類的實例,就會遭遇null。
    Blah();
    // 如果是在創建MoreDerived類的實例,就會遭遇null。
  }
  public virtual void Blah() { }
}
class Derived : Base
{
  readonly Foo derivedFoo = new Foo("Derived initializer");
  public DoIt()
  {
    derivedFoo.Bar();
  }
}
class MoreDerived : Derived
{
  public override void Blah() { DoIt(); }
}

看Base類的構造函數,如果按期望的順序執行,那麼在Base方法執行時,Derived類的實例成員並沒有得到初始化,此時就會有NullReference異常了。

而按照實際執行的順序,所有的實例成員都能確保被完整地初始化:)

當然了,如果readonly字段是在構造函數中進行的,那麼上面的確保機制就不復存在了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved