I realized something while I was working on a project. I thought it might be a useful idea, so I thought I'd write a post.
To help clarify the following, I will be using the terminology for generics as it appears in System.Reflection. List<T> is an example of a "generic type definition", because its type arguments have not been specified. Whereas List<string> is a "generic type". T is a "type parameter" and string is a "type argument".
In .NET (C#), while a generic type definition can be referenced using typeof (e.g. typeof(List<>)) in order to get a Type object, you cannot declare a variable with a generic type definition type. This is because the generic type definition isn't really a type in .NET, it's more of a template for a type. Furthermore, generic types from the same definition do not automatically inherit from a common base class. This presents a problem when you want to be able to reference an object of a generic type without knowing its type arguments.
I ran into this problem yesterday. I realized, however, that since types can be distinguished by how many type parameters they have, a type that is not generic and a generic type definition with one or more type arguments can both be defined without causing a naming conflict. This can commonly be seen with the old System.Collections classes and interfaces and the new generic System.Collection.Generic types (e.g. Stack vs. Stack<T>).
So I performed a little trick taking advantage of this in order to solve the above problem. Here's some example code:
public abstract class TransactionField
{
}
public class TransactionField<T> : TransactionField
{
private T m_Value;
public T Value { get { return m_Value; } set { m_Value = value; } }
}
Now an object of any TransactionField<T> type can be referenced as simply TransactionField. This is great for referencing, but what about that Value property? What if you need to be able to access that property for any TransactionField<T> object? Check this out:
public abstract class TransactionField
{
public object Value { get { return _Value; } set { _Value = value; } }
protected abstract object _Value { get; set; }
}
public class TransactionField<T> : TransactionField
{
private T m_Value;
public new T Value { get { return m_Value; } set { m_Value = value; } }
protected override object _Value { get { return Value; } set { Value = (T)value; } }
}
It gets a little confusing with the naming. But basically, TransactionField defines an protected abstract property _Value so that it can access the value stored in types derived from it. It also defines a public property Value which provides public access to the value stored in the derived type via _Value. Finally, TransactionField<T> defines a new property with the same name Value except this one uses the type argument instead of object.
Now at this point, you might be wondering what the point of using generics at all in this scenario is. Well, TransactionField<T> replaces the Value property with a typesafe one. When you deal with the generic type, you won't need to cast any values. This approach is the best of both worlds. The object can be dealt with as an object of a generic type or a non-generic type, depending on what is needed for the code that is using the object.