Reader Question: Member Initializers vs. Constructors

I received a very interesting question from a reader earlier this week:

I have a question for you about Item 12 in Effective C# (2nd edition), "Prefer Member Initializers to Assignment Statements".  Here is my problem:

I recently inherited a very complicated application in which I had to find and fix a bug.  It was incredibly painful.  The application contained many classes that contained other classes that contained other classes that inherited from other classes that inherited from other classes.  I think you get the idea.  I found the member initializers particularly painful.  When code was about to create a new object, and I’d expect to step into its constructor, instead I’d end up stepping through a maze of member initializers for other classes, with no idea where I was, or why I was there, before I’d get to the constructor of the class I expected to be in.

After that experience, I swore that I would not use member initializers ever, and always initialize members in my constructors, so that when stepping through the code there would be some sense of chronological order of object creation that made sense.

Item 12 tells me to prefer member initializers.  You give 2 reasons why: to minimize the risk of omitting an initialization with multiple constructors, and to initialize variables as early in time as possible.  I guess my question is why is it important to initialize variables as early in time as possible???  I’d like to purposely delay initialization until I’m in the constructor to help debugging seem more natural, but if there is a real benefit of initializing earlier, I’d rather do that.  Are there any memory use or compiler optimization benefits to using member initializers?

This is a great question, because it does highlight language features, coding practices, and tools.

First the language features: the C# designers made the decision that variables in an object are initialized before any code in that object executes. You can initialize a variable with a field initializer, or accept the default initialization of the 0 bit pattern. That means field initializers execute before any constructor code. As I wrote in Effective C#, initializing object fields using field initializers insures that all are definitely initialized as early as possible.

I recommended initializing fields as early as possible, because it minimizes the chance of null reference exceptions. If you initialize fields before any code executes, you cannot write code that accesses those fields before you initialize the fields. The more code (even in constructors) that you may execute before initializing fields, the more likely you introduce those kinds of bugs. As code bases grow, developers may add new constructors, or add new method calls in constructors. Any of those additions can dereference unitialized fields, causing bugs. I expand on this in C# Puzzlers, where I discuss virtual method calls in constructors.

However, this coding practice can make it hard to debug.  There’s a little-known feature that really helps:  You can set breakpoints on field initializers. It can be confusing, unless you remember the order of initialization. That’s why I went into extensive detail in Effective C# on the initialization order of objects created using C# (and interacting with VB.NET, where the order is different).  It can be somewhat tricky, especially with very deep hierarchies. But, hopefully, you have to debug initialization code less often.

Finally, note that the title says “Prefer Field Initializers”. It’s not an absolute rule: some fields are much more naturally initialized using constructors. But, absent good reason to pull the code into a constructor, the field initializer is preferred.

6 comments to Reader Question: Member Initializers vs. Constructors

  • Is there any performance benefits to “field initialization” as oppose to constructor initialization?

  • Michael Chandler

    How many times have you seen bugs caused by missing ctor initialisation? I’d say it’s *very* rare compared to null refs from methods returning null.

    Personally I’d take the ctor initialisation any day of the week. If you’re writing tests then any initialisation bugs should be picked up pretty quickly and are unlikely to reoccur.

    If you’re using ctor then, as with most code, try to avoid duplication. Add the initialisation into the empty ctor and call : this() or do it within a method.

  • Bill Wagner

    @A.Arce: There can be performance benefits. If you initialize fields that allocate memory multiple times, that can increase memory pressure. However, that’s generally not a big factor, and I wouldn’t make these kinds of changes for performance reasons without profiling, and knowing that it fixes a real problem.

    @Michael: I have seen quite a few initialization bugs in field, although fewer than those caused by not checking return values which may be null.

    Testing does help, but here’s the scenario where these kinds of bugs most often sneak through:
    1. Someone adds a new constructor, and new unit tests that exercise the new constructor.
    2. All the old tests pass, but they are also using the existing constructors.
    3. The new tests don’t exercise some method that assumes a field has been allocated.

    It’s the situation where the unit tests all pass, but there’s now new interactions, and not all those permutations are tested completely.

  • Member initializers means you have properties publicly exposed. Worse than this, they’re also open to being modified publicly also. This goes against encapsulation and information hiding. These concepts are OO 101 stuff, and we learn early on that we should not do it.

  • Bill Wagner

    @Mark, you’re confusing two different concepts. *Field* initializers, which are being talked about here allow you to initialize an object field where you declare it:

    public class Foo
    {
    private int bar = 5;
    }

    Your comment discusses Member initializers. Even there, I disagree that public properties are bad. Public Read/Write properties have their place in OO design.

  • that’s generally not a big factor, and I wouldn’t make these kinds of changes for performance reasons without profiling, and knowing that it fixes a real problem.Great article thanks.