Let's say that I'm building a generic Table<T>
class. Here are my requirements for the container:
- Type
T
must be a reference (or nullable, whichever you prefer) type, in order to usenull
in place ofT
. This gives me thewhere T: class
constraint. - Type
T
must beIComparable
, since I want to compare the values in the table, which gives me another constraint ofwhere T : IComparable
. (There could be more interface constraints.) - The generic type must be able to specialize to an interface, which means I can't further restrict this to inherit from some specific (even abstract) class.
In summary, the table would be defined as class Table<T> where T: class, IComparable
. I also need to create an instance of T which represents a default value. The default doesn't necessarily mean just a default, there could be two different default values, just think of this as creating some representative predefined values for the given type T (this could also be something like max/min value.)
To reiterate my last point, I'd like to be able to use this to store types which don't share a common parent. For example, consider the following type hierarchy
A
/ | \
B C D
Now let's say that I only want to store types B
and C
, and nothing else. The ad-hoc solution would be to change the class hierarchy to look like this
A
/ \
E D
/ \
B C
and now you could say that I can use Table<E>
. But what if the classes B
and C
are completely unrelated? Or maybe they are already in an inheritance hierarchy of their own, and just share a common fact that they need to be stored in this table.
Possible solution but not ideal
The solution to this problem would be to define an interface that they would both implement, and use that as the generic type specialization. For example
class A {}
class B : A, IStorable {}
class C : A, IStorable {}
class D : A {}
interface IStorable {}
With this interface, I can now easily do Table<IStorable>
and be sure that only the B
and C
types are being stored in the table, which is great. But this is where I got stuck. Using the interface, I no longer have a way to say the type stored in the table should be able to create a default value. In this particular case, it could be that the default value for IStorable
is new B(42)
, but I have no way of expressing that in the generic Table<T>
, since it knows nothing about my type T
. If I could say that T
has to implement a static method, that could be used as a factory method, that would solve the problem, but static methods can't be placed in an interface in C#, so that is out of question as well.
The only workaround I found so far, is that the Table<T>
has a constructor accepting Func<T>
, which would be a function that can create the default value, but this feels very ad-hoc as well. Just to make it clear how that would look like
class Table<T> where T: class, IComparable, IStorable {
Table(Func<T> factory) { ... }
}
Is there a better way of using generic, such that I can create instances of the type T
, while also allowing T
to be an interface? Just a disclaimer, the default
operator is not good enough, since it only returns null
for reference types, and requiring a parameterless constructor via where T : new()
is not good enough as well, since that doesn't allow me to specialize Table<T>
on an interface type.