9

I just spent hours being confused by an NullReferenceException where I thought there shouldn't be one. I was constructing a class like so:

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

where

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

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

Basically my IL was the following:

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

and I finally figured that it must be that bar was not being instantiated. I constructed my class in C# and compiled it and found the only difference was that the following should be above the IL above:

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

With these lines, my code now works as expected.

So my questions are:

  1. Why do we need to explicitly call the base constructor to instantiate fields?
  2. Is there a legitimate use of not calling the base constructor?
  3. ...and if not, why does the CLR accept it as a valid program?
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
dav_i
  • 27,509
  • 17
  • 104
  • 136
  • 4
    @PatrickHofman Best edit ever :P – dav_i Sep 11 '14 at 13:15
  • The base class constructor is **always** called. If you don't write it explicitly in your C# code then the compiler does it for you. And *do* note that there always is one, System.Object has a constructor. Very easy to see with ildasm.exe, the tool you always want to use first before you start generating your own MSIL. – Hans Passant Sep 11 '14 at 13:21
  • 1
    @HansPassant in C# it is, yes; in IL: not so much – Marc Gravell Sep 11 '14 at 13:22
  • Hmm, no, even C++ compilers generate unnecessary base constructor calls. It is the job of the code generator to eliminate them. The jitter does that well, it doesn't need any help. That the verifier doesn't require it is just a detail, the zero-initialization promise is enough to generate verifiable code. That NRE is guaranteed. – Hans Passant Sep 11 '14 at 13:31

1 Answers1

6
  1. because it isn't automatic, and it allows compilers and IL code to decide both when and if to call the base constructor
  2. well, you can actually instantiate types without using any constructors whatsoever... edge case, sure; but allowed
  3. because it is allowed

Note that you can actually emit a simple constructor (including base call) in a single method:

typeBuilder.DefineDefaultConstructor();
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900