This is about c# constructors and has been asked before a couple of times. However, the questions and answers never really fit my scenario:
public abstract class BaseClass {
public List<int> MyIntegers { get; set; }
public BaseClass()
{
this.MyIntegers = new List<int>();
}
public BaseClass(int initialInteger) : this()
{
this.MyIntegers.Add(initialInteger);
}
}
public class DerivedClass : BaseClass
{
public List<string> MyStrings { get; set; }
public DerivedClass()
{
this.MyStrings = new List<string>(); // does never get initialized
}
public DerivedClass(int initialInteger) : base(initialInteger)
{
this.MyStrings.Add(initialInteger.ToString()); <-- exception because DerivedClass() is never called
}
}
// In Program.cs (or where ever)
var derivedClass = new DerivedClass(10);
The code above will fail because the parameterless constructor of DerivedClass
is never called. So, List<string> MyStrings
is not initialized.
I know I can't do constructor chaining with base() and this() at the same time. But of course I could do this:
// Workaround
public DerivedClass(int initialInteger) : base(initialInteger)
{
this.MyStrings = new List<string>(); // redundant
this.MyStrings.Add(initialInteger.ToString());
}
This works, but it's pretty ugly because I am using redundant code. It seems ok here in the simple sample, but in my real application I would have to add a lot of redundant code. Not nice.
How would you solve this elegantly?
CodePudding user response:
As a rule of thumb, a constructor with few parameter should delegate to one with more parameters, ideally having one single constructor that does all the actual initialization.
public abstract class BaseClass {
public List<int> MyIntegers { get; set; }
public BaseClass() : this(new List<int>()) { }
public BaseClass(int initialInteger) : this(new List<int>(){initialInteger}) { }
protected BaseClass(List<int> myIntegers) => MyIntegers = myIntegers;
}
public class DerivedClass : BaseClass
{
public List<string> MyStrings { get; set; }
public DerivedClass () : this(new List<string>(), new List<int>()) { }
public DerivedClass (int initialInteger) : this(new List<string>(){initialInteger},new List<int>(){initialInteger}) { }
protected DerivedClass (List<string> myStrings, List<int> myIntegers) : base(myIntegers) => MyStrings = myStrings;
}
The downside with this is that some initialization-logic is duplicated, i.e. the derived class needs to create a list of integers to give to the base-class. If your creation logic is to complicated it might hint that your model is problematic, or that you should extract some of the creation logic to a separate factory class. Another tool that might be useful is an inversion of control container.
This assumes the given problem is a placeholder for something else, since this specific problem could easily be solved by just taking a params int[] initialValues
as Damien_The_Unbeliever suggest in the comments.