通用基础,协方差
Generic Bases, Covarience
我有一个通用基础 class,其他通用 class 继承自该基础:
public class B<T> {
private List<T> parent;
public bool IsInParent() { return parent.Contains(this); }
public void Attach() { parent.Add(this); }
}
两个 this
给出错误“无法从 B<T>
转换为 T
。我明白这是因为 this
可能是其他东西T(协方差)因此不会进入List<T>
。我明白了。
如果我声明 T
继承自 B<T>
,则第一个 this
没有错误,但添加 this
的错误仍然存在。
public class B<T> where T : B<T> { ..... ]
现在,如果每个 T
都继承自 B<T>
,那么每个 B<T>
都是 T
,因此应该进入 List<T>
。
我做错了什么?
The two this give the error "cannot convert from B<T>
to T
. I understand that this is due to the fact that this
could be something other that T
(covariance) and therefore won't go into a List<T>
.
我觉得你没看懂。
首先:this
的类型是B<T>
。想象一下,我们把它变得更具体,而不是抽象 B<T>
:
class Comparer<T> // I can compare two things of type T.
{
....
this
inside Comparer<T>
将是 Comparer<T>
类型。如果您有 List<T>
,您可以将 T
放入列表中。假设 T
是 Apple
。 this
可以比较两个苹果。 List<T>
可以包含两个苹果。但是 List<T>
不能包含两个比较器 apples 因为那不是它的类型!
其次,你不明白"covariance"这个词是什么意思。我不知道你认为这意味着什么,但我猜你认为 "covariance" 意味着 "assignment compatibility"。这通常是人们认为 "covariance" 的意思,但他们的意思是错误的。
"Assignment compatibility" 是 属性 类型 Apple
的值可以进入类型 Fruit
的变量,因为 Apple
是 与 Fruit
的存储兼容的分配。 那不是协方差。
协方差是属性泛型类型保留赋值兼容关系。
即:Apple
可以进入 Fruit
类型的变量,因此 IEnumerable<Apple>
可以进入 IEnumerable<Fruit>
类型的变量。分配兼容性 的关系被保留,因为类型通过使它们通用 而变化,因此 IEnumerable<T>
是 co-variant;事物朝着同一个方向变化。
If I state that T
inherits from B<T>
there is no error for the first this but the error for the Add
this
remains.
首先:不要这样做。这是一个糟糕的模式。它是 C++ 模式的变体,称为 "curiously recurring template pattern"。我已经看到它在 C# 中使用了很多很多次,而且几乎总是使用错误。躲开它。它使您的类型变得复杂、难以使用、难以理解,并且使您认为您对自己的类型有 C# 不支持的约束。
Now surely if every T
inherits from B<T>
, then every B<T>
is a T
and therefore should go into a List<T>
.
现在您也许开始理解为什么这种模式如此糟糕。它让你相信完全疯狂的虚假事物,因为它太令人困惑了!
让我们再次让您的句子不那么混乱。我们将 T
替换为 Apple
,将 B<T>
替换为 Fruit
,我们有:
Now surely if every apple is a kind of fruit, then every fruit is an apple and therefore should go into a bowl of apples".
你的结论是苹果是水果,所以所有的水果都是苹果,所以你可以把一根香蕉放到一碗苹果里,它仍然是一碗苹果。说白了就是无稽之谈。
What am I doing wrong?
您构建的通用类型非常复杂,您无法正确理解它们。这意味着 使用 您的代码的人也无法理解它们。寻找更简单的方法来解决您的问题。
我有一个通用基础 class,其他通用 class 继承自该基础:
public class B<T> {
private List<T> parent;
public bool IsInParent() { return parent.Contains(this); }
public void Attach() { parent.Add(this); }
}
两个 this
给出错误“无法从 B<T>
转换为 T
。我明白这是因为 this
可能是其他东西T(协方差)因此不会进入List<T>
。我明白了。
如果我声明 T
继承自 B<T>
,则第一个 this
没有错误,但添加 this
的错误仍然存在。
public class B<T> where T : B<T> { ..... ]
现在,如果每个 T
都继承自 B<T>
,那么每个 B<T>
都是 T
,因此应该进入 List<T>
。
我做错了什么?
The two this give the error "cannot convert from
B<T>
toT
. I understand that this is due to the fact thatthis
could be something other thatT
(covariance) and therefore won't go into aList<T>
.
我觉得你没看懂。
首先:this
的类型是B<T>
。想象一下,我们把它变得更具体,而不是抽象 B<T>
:
class Comparer<T> // I can compare two things of type T.
{
....
this
inside Comparer<T>
将是 Comparer<T>
类型。如果您有 List<T>
,您可以将 T
放入列表中。假设 T
是 Apple
。 this
可以比较两个苹果。 List<T>
可以包含两个苹果。但是 List<T>
不能包含两个比较器 apples 因为那不是它的类型!
其次,你不明白"covariance"这个词是什么意思。我不知道你认为这意味着什么,但我猜你认为 "covariance" 意味着 "assignment compatibility"。这通常是人们认为 "covariance" 的意思,但他们的意思是错误的。
"Assignment compatibility" 是 属性 类型 Apple
的值可以进入类型 Fruit
的变量,因为 Apple
是 与 Fruit
的存储兼容的分配。 那不是协方差。
协方差是属性泛型类型保留赋值兼容关系。
即:Apple
可以进入 Fruit
类型的变量,因此 IEnumerable<Apple>
可以进入 IEnumerable<Fruit>
类型的变量。分配兼容性 的关系被保留,因为类型通过使它们通用 而变化,因此 IEnumerable<T>
是 co-variant;事物朝着同一个方向变化。
If I state that
T
inherits fromB<T>
there is no error for the first this but the error for theAdd
this
remains.
首先:不要这样做。这是一个糟糕的模式。它是 C++ 模式的变体,称为 "curiously recurring template pattern"。我已经看到它在 C# 中使用了很多很多次,而且几乎总是使用错误。躲开它。它使您的类型变得复杂、难以使用、难以理解,并且使您认为您对自己的类型有 C# 不支持的约束。
Now surely if every
T
inherits fromB<T>
, then everyB<T>
is aT
and therefore should go into aList<T>
.
现在您也许开始理解为什么这种模式如此糟糕。它让你相信完全疯狂的虚假事物,因为它太令人困惑了!
让我们再次让您的句子不那么混乱。我们将 T
替换为 Apple
,将 B<T>
替换为 Fruit
,我们有:
Now surely if every apple is a kind of fruit, then every fruit is an apple and therefore should go into a bowl of apples".
你的结论是苹果是水果,所以所有的水果都是苹果,所以你可以把一根香蕉放到一碗苹果里,它仍然是一碗苹果。说白了就是无稽之谈。
What am I doing wrong?
您构建的通用类型非常复杂,您无法正确理解它们。这意味着 使用 您的代码的人也无法理解它们。寻找更简单的方法来解决您的问题。