A Common bug in C# when using Inheritance and Constructor Bodies

Posted in software by Christopher R. Wirz on Wed Jun 29 2011



Inheritance is a great feature of any object-oriented language. C# adds some modern flair with the ability to have inline constructor chains as well as constructor bodies and optional parameters. This has led to a source of bugs in a number of code-bases in which developers are transitioning from another language.

One such bug involves the use of constructor chains on base and dervied classes - along with constructor bodies in instantiation of the derived class.

Note: constructor chains execute right to left, top to bottom.

Observe the following example:


class BaseClass
{
	protected int _index = 0;
	public virtual int Index
	{
		get
		{
			return _index;
		}
		set
		{
			if (_index != value)
			{
				_index = value;
				Console.WriteLine("BaseClass.set_Index(" + Index + ") called");
			}
		}
	}

	private BaseClass()
	{
		Console.WriteLine("BaseClass() constructor called");
	}

	public BaseClass(int index = 1) : this()
	{
		Console.WriteLine("BaseClass(int index) constructor called");
		Index = index;
	}
}
class DerivedClass : BaseClass
{
	public virtual string Title { get; set; } = string.Empty;
	public override int Index
	{
		set
		{
			if (_index != value)
			{
				_index = value;
				Console.WriteLine("DerivedClass.set_Index(" + Index + ") called");
			}
		}
	}

	public DerivedClass(string title = null, int index = 2) : base()
	{
		Console.WriteLine("DerivedClass(string title, int index) constructor called");
		Index = index;
		Title = title;
	}

	public static void Main()
	{
		BaseClass dc = new DerivedClass(index: 3) // Constructor
		{
			Index = 4, // Constructor body
			Title = "Demo Title"
		};
		
		// Just because we want to know what happens when we upcast
		(dc as BaseClass).Index = 5;
	}
}

Running the above code produces the following results:

BaseClass() constructor called
BaseClass(int index) constructor called
DerivedClass.set_Index(1) called
DerivedClass(string title, int index) constructor called
DerivedClass.set_Index(3) called
DerivedClass.set_Index(4) called
DerivedClass.set_Index(5) called
Note: The value of 3 was passed in the constructor but yet the value of 1 was set during initialization.

Wait, what happened?

In this case, the optional index value was not set, and so the default value of 1 was passed into the base constructor. The private parameterless base constructor was called in the base class's optional parameter constructor chain.

The fix is to change


public DerivedClass(string title = null, int index = 2) : base()

to


public DerivedClass(string title = null, int index = 2) : base(index)

or (somewhat redundantly, but more self-documenting)


public DerivedClass(string title = null, int index = 2) : base(index: index)

Making the change correctly calls the base constructor which is passed the integer value of 3.

BaseClass() constructor called
BaseClass(int index) constructor called
DerivedClass.set_Index(3) called
DerivedClass(string title, int index) constructor called
DerivedClass.set_Index(4) called
DerivedClass.set_Index(5) called

Bug: fixed!