19

每隔一段时间,我就会通过向其添加自引用(“自反”)类型参数约束来使简单接口变得更加复杂。例如,我可能会这样:

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# 代码模式可以实现相同的效果或类似的东西,但以更容易掌握的方式?


*)此 Code Pattern 可能不直观且难以理解,例如:

  • 该声明X<T> where T : X<T>似乎是递归的,有人可能想知道为什么编译器不会陷入无限循环,推理​​,“如果T是一个X<T>,那么X<T>真的是一个X<X<…&lt;T>…&gt;>。” (但约束显然不会像那样得到解决。)

  • 对于实现者来说,应该指定什么类型来代替TImpl. (约束最终会解决这个问题。)

  • 一旦您在混合中添加更多类型参数和各种通用接口之间的子类型关系,事情就会很快变得难以管理。

4

2 回答 2

19

主要优点:实现类型现在可以引用自身而不是其基类型,从而减少了类型转换的需要

虽然看起来类型约束引用了它自己,但它强制实现类型做同样的事情,但实际上它不是这样做的。人们使用这种模式来尝试表达“对此方法的覆盖必须返回覆盖类的类型”形式的模式,但这实际上并不是类型系统表达或强制执行的约束。我在这里举个例子:

https://ericlippert.com/2011/02/02/curiouser-and-curiouser/

虽然这非常好,但我也注意到这些类型参数约束不直观,并且在更复杂的场景中变得非常难以理解

是的。我尽量避免这种模式。很难推理。

有谁知道另一种 C# 代码模式可以实现相同的效果或类似的东西,但以更容易掌握的方式?

不在 C# 中,不。如果您对这类事情感兴趣,您可能会考虑查看 Haskell 类型系统;Haskell 的“高级类型”可以代表这些类型的模式。

该声明X<T> where T : X<T>似乎是递归的,有人可能想知道为什么编译器不会陷入无限循环,推理​​,“如果T是一个X<T>,那么X<T>真的是一个X<X<…&lt;T>…&gt;>。”

在推理这种简单的关系时,编译器永远不会陷入无限循环。但是,具有逆变的泛型类型的名义子类型通常是不可判定的。有一些方法可以强制编译器无限回归,而 C# 编译器不会检测到这些并在开始无限之旅之前阻止它们。(然而。我希望在 Roslyn 编译器中为此添加检测,但我们会看到。)

如果您对此感兴趣,请参阅我关于该主题的文章。您还需要阅读链接到的论文。

https://ericlippert.com/2008/05/07/covariance-and-contravariance-part-11-to-infinity-but-not-beyond/

于 2012-01-15T01:42:26.370 回答
7

不幸的是,没有办法完全防止这种情况,ICloneable<T>没有类型约束的泛型就足够了。您的约束仅将可能的参数限制为本身实现它的类,这并不意味着它们是当前正在实现的类。

换句话说,如果 a Cowimplements ICloneable<Cow>,您仍然可以轻松地 make Sheepimplement ICloneable<Cow>

我会简单地使用ICloneable<T>没有限制,原因有两个:

  1. 我严重怀疑您是否会犯使用错误类型参数的错误。

  2. 接口旨在成为代码其他部分的合同,而不是用于在自动驾驶仪上进行编码。如果代码的一部分是期望的ICloneable<Cow>并且你传递了一个Sheep可以做到这一点的代码,那么从那时起它似乎是完全有效的。

于 2012-01-15T02:34:13.220 回答