2014-09-11 74 views
9

我剛剛花了幾個小時被NullReferenceException弄糊塗了,我認爲那裏不應該有一個。我正在建造的一類,像這樣:爲什麼我們需要在MSIL中顯式調用父構造函數?

public class MyClass : MyBase<Foo> 
{ 
    public MyClass() 
    { 
     base.Method(Foo.StaticField); 
    } 
} 

其中

public class MyBase<T> 
{ 
    private SomeObject bar = new SomeObject(); 

    public void Method(object o) 
    { 
     this.bar.AnotherMethod(o); // exception thrown here 
    } 
} 

基本上我的IL是以下幾點:

ctorIl.Emit(OpCodes.Ldarg_0); 
ctorIl.Emit(OpCodes.Ldsfld, staticField); 
ctorIl.Emit(OpCodes.Box, typeof(FieldType)); 
ctorIl.Emit(OpCodes.Call, parentMethod); 
ctorIl.Emit(OpCodes.Ret); 

,我終於想通它必須是bar沒有被實例化。我構建了我的類在C#中並編譯了一下,發現唯一的區別是,下面應該是IL上述上面:

ctorIl.Emit(OpCodes.Ldarg_0); 
ctorIl.Emit(OpCodes.Call, parentCtor); 
// as above 

有了這些行,我的代碼現在將按預期。

所以我的問題是:

  1. 爲什麼我們需要顯式調用基類的構造實例字段?
  2. 有沒有調用基礎構造函數的合法用法?
  3. ...如果沒有,爲什麼CLR接受它作爲一個有效的程序?
+4

@PatrickHofman最佳編輯有史以來:P – 2014-09-11 13:15:42

+0

基類構造函數是**總是**被調用。如果你沒有在你的C#代碼中明確寫出它,那麼編譯器會爲你做。 * *注意總是有一個,System.Object有一個構造函數。使用ildasm.exe很容易看到,在開始生成自己的MSIL之前,您始終需要使用該工具。 – 2014-09-11 13:21:11

+1

@HansPassant在C#中是的,是的;在IL中:並非如此 – 2014-09-11 13:22:01

回答

6
  1. ,因爲它不是自動的,它可以讓編譯器和IL代碼來決定兩個如果調用基類的構造
  2. 好,你可以真正實例類型而無需使用任何構造函數......邊緣案例,當然;但允許
  3. ,因爲它允許

注意,你實際上可以在一個單一的方法發出一個簡單的構造(包括鹼基調用):

typeBuilder.DefineDefaultConstructor(); 
+0

啊,所以'DefineDefaultConstructor'基本上做了我在這裏自動完成的前兩行IL? – 2014-09-11 13:23:06

+0

@dav_i yep;來自MSDN:「定義默認構造函數,此處定義的構造函數將簡單地調用父項的默認構造函數。」 – 2014-09-11 13:23:17

+0

RTFM應該不? :P謝謝馬克。 – 2014-09-11 13:24:06

相關問題