假设我正在构建一个通用的Table<T>
类。以下是我对容器的要求:
T
必须是引用(或可以为空,取决于您喜欢的)类型,才能使用null
代替T
。这给了我where T: class
约束。T
必须为IComparable
,因为我想比较表中的值,这给了我where T : IComparable
的另一个约束。 (可能有更多的接口限制。)总之,该表将定义为class Table<T> where T: class, IComparable
。我还需要创建一个代表默认值的T实例。 默认并不一定只是默认值,可能有两个不同的默认值,只需将其视为为给定类型创建一些有代表性的预定义值T (这也可能是最大/最小值。)
重申我的最后一点,我希望能够使用它来存储不共享父母的类型。例如,请考虑以下类型层次结构
A
/ | \
B C D
现在让我们说我只想存储类型B
和C
,而不是其他内容。 ad-hoc解决方案是将类层次结构更改为如下所示
A
/ \
E D
/ \
B C
现在你可以说我可以使用Table<E>
。但是如果课程B
和C
完全不相关怎么办?或者他们可能已经处于自己的继承层次结构中,只是分享了一个常见的事实,即它们需要存储在此表中。
这个问题的解决方案是定义一个他们都将实现的接口,并将其用作泛型类型专门化。例如
class A {}
class B : A, IStorable {}
class C : A, IStorable {}
class D : A {}
interface IStorable {}
通过此界面,我现在可以轻松地执行Table<IStorable>
并确保只有B
和C
类型存储在表格中,这很棒。但这就是我陷入困境的地方。使用界面,我不再有办法说表中存储的类型应该能够创建默认值。在这种特殊情况下,IStorable
的默认值可能是new B(42)
,但我无法在通用Table<T>
中表达,因为它对我的类型一无所知{ {1}}。如果我可以说T
必须实现静态方法,那可以用作工厂方法,这将解决问题,但静态方法不能放在C#中的接口中,所以这也是不可能的。
我到目前为止找到的唯一解决方法是T
有一个构造函数接受Table<T>
,这是一个可以创建默认值的函数,但这也是非常特别的。只是为了弄清楚它会是什么样子
Func<T>
是否有更好的使用通用的方式,以便我可以创建class Table<T> where T: class, IComparable, IStorable {
Table(Func<T> factory) { ... }
}
类型的实例,同时还允许T
成为接口?只是免责声明,T
运算符不够好,因为它只返回default
作为引用类型,并且需要通过null
的无参数构造函数也不够好,因为这不允许我要在接口类型上专门化where T : new()
。