How can I require a generic type to have a class method in C#?

265 Views Asked by At

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 use null in place of T. This gives me the where T: class constraint.
  • Type T must be IComparable, since I want to compare the values in the table, which gives me another constraint of where 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.

0

There are 0 best solutions below