Problem
How to create a list of generic types?
Let's say you have a base class called Parameter. But you can have different parameter types, hence different implementations of operands (+, -, etc). You can have Parameter<int>, Parameter<string>, etc.
How would you create a list of these generics? One solution would be to create a base class with the same functionality of the generic type and just hide the context parameter.
public class Parameter
{
public virtual object Value { get; private set; }
public Parameter(object value)
{
this.Value = value;
}
}
public class ParameterGen<T> : Parameter
{
public new T Value { get; private set; }
public ParameterGen(T value) : base(value)
{
this.Value = mult(value);
}
}Now you can do this:
List<Parameter> parameters = new List<Parameter>
{
new Parameter("a value"),
new ParameterGen<int>(2)
};What is wrong with this? It seems that you hid the Value parameter, all works fine. To find out what this exactly means try the following. Add a modifier of the value in the generic class:
public class ParameterGen<T> : Parameter
{
public new T Value { get; private set; }
public ParameterGen(T value, Func<T, T> mult) : base(value)
{
this.Value = mult(value);
}
}ParameterGen<int> p = new ParameterGen<int>(2, (x) => x * 2);
Console.WriteLine(p.Value);
Console.WriteLine(((Parameter)p).Value);When you hide a variable, it didn't just magically disappear or transform into a new one — it's just compiler-ignored. A new variable is created that is not related to the one from the base class. The expected results are 4 and 2, so by doing this you duplicated all your values, which is a clear source of errors.
Solution
The proper solution is to use an abstract base class with an abstract GetValue() method, and override it in the generic subclass. This avoids hiding and keeps the list polymorphic cleanly:
public abstract class Parameter
{
public abstract object GetValue();
}
public class ParameterGen<T> : Parameter
{
public T Value { get; set; }
public override object GetValue() => Value;
public ParameterGen(T value)
{
this.Value = value;
}
}
class Program
{
static void Main(string[] args)
{
List<Parameter> list = new List<Parameter>
{
new ParameterGen<int>(2), new ParameterGen<string>("da")
};
Console.WriteLine(list[0].GetValue());
}
}