C# constructor chaining - changing the order of execution

Sean Worle picture Sean Worle · Mar 25, 2011 · Viewed 7.3k times · Source

I want to know how to change the order of execution when chaining constructors in C#. The only methods I have seen require the chained constructor to be called first, outside of the current constructor.

Specifically, take the following example:

public class Foo {
  private static Dictionary<string, Thing> ThingCache = new Dictionary<string, Thing>();
  private Thing myThing;

  public Foo(string name) {
    doSomeStuff();
    if (ThingCache.ContainsKey(name)) {
      myThing = ThingCache[name];
    } else {
      myThing = ExternalStaticFactory.GetThing(name);
      ThingCache.Add(name, myThing);
    }
    doSomeOtherStuff();
  }

  public Foo(Thing tmpThing) {
    doSomeStuff();
    myThing = tmpThing;
    doSomeOtherStuff();
  }
}

Ideally, I'd like to reduce code repetition by doing this (note, I admit that in this contrived example, not much code is saved, but I am working with code that would benefit much more. I use this example for clarity):

public class Foo {
  private static Dictionary<string, Thing> ThingCache = new Dictionary<string, Thing>();
  private Thing myThing;

  public Foo(string name) {
    if (ThingCache.ContainsKey(name)) {
      this(ThingCache[name]);
    } else {
      this(ExternalStaticFactory.GetThing(name));
      ThingCache.Add(name, myThing);
    }
  }

  public Foo(Thing tmpThing) {
    doSomeStuff();
    myThing = tmpThing;
    doSomeOtherStuff();
  }
}

This is possible in VB .Net, but C# doesn't let me call a constructor in the middle of another constructor - only at the beginning using the Foo() : this() syntax.

So my question is, how does one control the order of constructor calling when chaining constructors, rather than using the colon syntax, which can only call the other constructor first?

Answer

Ergwun picture Ergwun · Mar 25, 2011

You can't call constructors inside other constructors. A constructor can only chain another constructor to be called directly before it.

However, to solve your particular issue, you can make a private constructor that can accept both a types of argument, and have your two original constructors both simply chain this one, passing null for the missing argument.

This has the advantage over calling private initialisation methods that it plays nicely with readonly fields (i.e. it still works if myThing is a readonly field):

public class Foo
{
    private static Dictionary<string, Thing> ThingCache =
        new Dictionary<string, Thing>();
    private Thing myThing;

    public Foo(string name)
        : this(null, name)
    {
    }

    public Foo(Thing tmpThing)
        : this(tmpThing, null)
    {
    }

    private Foo(Thing tmpThing, string name)
    {
        if (tmpThing == null && name == null)
        {
            throw new System.ArgumentException(
                "Either tmpThing or name must be non-null.");
        }

        doSomeStuff();
        if (tmpThing != null)
        {
            myThing = tmpThing;
        }
        else
        {
            if (ThingCache.ContainsKey(name))
            {
                myThing = ThingCache[name];
            }
            else
            {
                myThing = ExternalStaticFactory.GetThing(name);
                ThingCache.Add(name, myThing);
            }
        }
        doSomeOtherStuff();
    }
}

You could also use named or optional arguments if you are using C# 4.0:

http://msdn.microsoft.com/en-us/library/dd264739.aspx