我会经常通过添加自引用(“反身”)类型参数约束来使简单接口变得更加复杂。例如,我可能会将这个转换成:
interface ICloneable
{
ICloneable Clone();
}
class Sheep : ICloneable
{
ICloneable Clone() { … }
} //^^^^^^^^^^
Sheep dolly = new Sheep().Clone() as Sheep;
//^^^^^^^^
转化为:
interface ICloneable<TImpl> where TImpl : ICloneable<TImpl>
{
TImpl Clone();
}
class Sheep : ICloneable<Sheep>
{
Sheep Clone() { … }
} //^^^^^
Sheep dolly = new Sheep().Clone();
主要优势:实现类型(例如Sheep
)现在可以引用自身而不是其基础类型,减少了需要进行类型转换的需要(正如代码的最后一行所示)。
虽然这很好,但我也注意到这些类型参数约束并不直观,并且在更复杂的情况下很容易变得难以理解。*)
问题:有人知道另一个C#代码模式,可以以更易于理解的方式实现相同或类似的效果吗?
*) 这个代码模式可能不直观,难以理解,例如:
声明
X<T> where T : X<T>
看起来是递归的,人们可能会想知道为什么编译器不会陷入无限循环, 推理时会说:"如果T
是一个X<T>
,那么X<T>
实际上是一个X<X<…<T>…>>
。" (但约束显然不会像这样解决。)对于实现者来说,可能不明确应该在
TImpl
的位置指定哪种类型。(约束将最终处理这个问题。)一旦你添加了更多的类型参数和各种泛型接口之间的子类型关系,事情很快就变得难以管理。
Sheep
实现为class Sheep:ICloneable <Dog> {public Dog Clone(){...}}
,这可能不是一个人最初在ICloneable
接口中所想的。 - stakx - no longer contributingICloneable<T>
就足够了。正如下面的答案所示,额外的约束只会确保类型参数实现了ICloneable<>
接口,但不一定是相同的类型。 - stakx - no longer contributing