【问题标题】:Generic Bases, Covarience遗传基础,协方差
【发布时间】:2018-08-24 00:57:20
【问题描述】:

我有一个通用基类,其他通用类从该基类继承:

public class B<T> {

    private List<T> parent;

    public bool IsInParent() { return parent.Contains(this); }

    public void Attach() { parent.Add(this); }

}

两个this 给出错误“无法从B&lt;T&gt; 转换为T。我知道这是因为this 可能是T(协方差)以外的东西,因此不会进入List&lt;T&gt;。我明白了。

如果我声明T 继承自B&lt;T&gt;,则第一个this 没有错误,但添加this 的错误仍然存​​在。

public class B<T> where T : B<T> { ..... ]

现在可以肯定,如果每个T 都继承自B&lt;T&gt;,那么每个B&lt;T&gt; 都是T,因此应该进入List&lt;T&gt;

我做错了什么?

【问题讨论】:

  • "现在可以肯定,如果每个 T 都继承自 B&lt;T&gt;,那么每个 B&lt;T&gt; 都是 T,因此应该进入 List&lt;T&gt;" 你已经完全倒退了。想象一下,如果我说“现在肯定如果每个 int 都继承自对象,那么每个对象都是一个 int,因此应该进入List&lt;int&gt;”,我希望很明显这是错误的。正确的说法是“每个T都可以进入List&lt;B&lt;T&gt;&gt;”。
  • 感谢您的回复。这个比喻很有用。

标签: c# list generics add covariance


【解决方案1】:

这两个给出错误“无法从B&lt;T&gt; 转换为T。我知道这是因为this 可能是T(协方差)之外的其他东西,因此不会进入List&lt;T&gt;

我认为你不明白。

首先:this 的类型为 B&lt;T&gt;。想象一下,而不是抽象的B&lt;T&gt;,我们让它更具体:

class Comparer<T> // I can compare two things of type T.
{
    ....

this 内的Comparer&lt;T&gt; 将是Comparer&lt;T&gt; 类型。如果您有List&lt;T&gt;,则可以将T 放入列表中。假设TApplethis 可以比较两个苹果。 List&lt;T&gt; 可以包含两个苹果。但是List&lt;T&gt; 不能包含两个苹果比较器,因为那不是它的类型!

其次,你不明白“协方差”这个词是什么意思。我不知道您认为这意味着什么,但我的猜测是您认为“协方差”意味着“分配兼容性”。这就是人们通常认为“协方差”的含义,当他们对它的含义有误时。

“赋值兼容性”是Apple 类型的值可以进入Fruit 类型的变量的属性,因为Apple 与@987654343 的存储赋值兼容 @。 这不是协方差

协变是泛型类型保留赋值兼容性关系的属性。

即:Apple 可以进入Fruit 类型的变量,因此IEnumerable&lt;Apple&gt; 可以进入IEnumerable&lt;Fruit&gt; 类型的变量。分配兼容性的关系被保留为通过使它们通用来改变类型,因此IEnumerable&lt;T&gt;co-variant;事物朝着同一个方向变化

如果我声明 TB&lt;T&gt; 继承,则第一个 this 没有错误,但 Add this 的错误仍然存​​在。

首先:不要这样做。这是一个糟糕的模式。它是 C++ 模式的一种变体,称为“奇怪重复的模板模式”。我已经看到它在 C# 中使用了很多次,而且几乎总是用错了。躲开它。它使您的类型变得复杂、难以使用、难以理解,并且使您认为您对您的类型有一个 C# 不支持的约束。

现在可以肯定,如果每个T 都继承自B&lt;T&gt;,那么每个B&lt;T&gt; 都是T,因此应该进入List&lt;T&gt;

现在你也许开始明白为什么这种模式如此糟糕了。它让你相信完全疯狂的虚假事物,因为它是如此令人困惑!

让我们再次让您的句子不那么混乱。我们将T 替换为Apple,将B&lt;T&gt; 替换为Fruit,我们有:

现在可以肯定,如果每个苹果都是一种水果,那么每个水果都是一个苹果,因此应该放入一碗苹果中。

你的结论是苹果是水果,因此所有的水果都是苹果,所以你可以把一根香蕉放进一碗苹果里,它仍然是一碗苹果。显然这是荒谬的。

我做错了什么?

您正在构建的泛型类型非常复杂,以至于您无法正确理解它们。这意味着使用您的代码的人也将无法理解它们。找到一种更简单的方法来解决您的问题。

【讨论】:

  • 感谢您的回答:它解释了很多。我将尝试在没有“奇怪重复的模板模式”的情况下重新编码。