2014-09-11 33 views
9

Po prostu spędziłem wiele godzin, będąc zdezorientowanym przez NullReferenceException, gdzie myślałem, że nie powinno być. Byłem konstruowania klasę tak:Dlaczego musimy jawnie wywoływać konstruktora macierzystego w MSIL?

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

gdzie

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

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

Zasadniczo mój IL był następujący:

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); 

i ja w końcu pomyślałem, że musi być tak, że bar nie będąc utworzony. I zbudowany moją klasę w C# i skompilowane go i okazało się, że jedyną różnicą było to, że następujące dokumenty powinny znajdować się powyżej IL powyżej:

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

z tymi liniami, mój kod działa teraz zgodnie z oczekiwaniami.

Więc moje pytania to:

  1. Dlaczego musimy jawnie wywołać konstruktora bazowy instancji pola?
  2. Czy jest uzasadnione użycie nie wywoływania podstawowego konstruktora?
  3. ... a jeśli nie, dlaczego CLR akceptuje to jako ważny program?
+4

@PatrickHofman Najlepszy edit kiedykolwiek: P –

+0

Konstruktor klasy podstawowej jest zawsze ** zawsze wywoływany. Jeśli nie napiszesz tego bezpośrednio w swoim kodzie C#, kompilator zrobi to za Ciebie. I * nie * zauważ, że zawsze jest jeden, System.Object ma konstruktora. Bardzo łatwe do zobaczenia dzięki ildasm.exe, narzędziu, z którego zawsze chcesz skorzystać, zanim zaczniesz generować własny MSIL. –

+1

@HansPassant w języku C# jest, tak; w IL: nie tak dużo –

Odpowiedz

6
  1. , ponieważ nie jest automatyczna, a to pozwala kompilatory i kod IL decydować zarówno gdy jest i jeśli wywołać konstruktora bazowy
  2. dobrze, można rzeczywiście instancji typów bez użycia dowolne konstruktory ... przypadek krawędzi, na pewno; ale dozwolone
  3. ponieważ jest dozwolone

Zauważ, że rzeczywiście można emitować prosty konstruktor (w tym połączenia bazowej) w jednej metody:

typeBuilder.DefineDefaultConstructor(); 
+0

Ah tak "DefineDefaultConstructor' zasadniczo robi pierwsze dwa wiersze IL, które zrobiłem tutaj automagicznie? –

+0

@dav_i yep; z MSDN: "Definiuje domyślny konstruktor, zdefiniowany tutaj konstruktor po prostu wywoła domyślny konstruktor rodzica." –

+0

Czy RTFM nie powinienem? : P Dzięki Marc. –