c# - 通用基础,协方差
问题描述
我有一个通用基类,其他通用类从该基类继承:
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
但 Add 的错误this
仍然存在。
public class B<T> where T : B<T> { ..... ]
现在可以肯定的是,如果T
each 继承自B<T>
,那么 eachB<T>
就是 a T
,因此应该进入 a List<T>
。
我究竟做错了什么?
解决方案
这两个给出了错误“无法转换
B<T>
为T
.this
T
List<T>
我不认为你理解它。
第一:this
是类型B<T>
。B<T>
想象一下,我们把它变得更具体,而不是抽象:
class Comparer<T> // I can compare two things of type T.
{
....
this
insideComparer<T>
将是 type Comparer<T>
。如果您有 a List<T>
,则可以将 aT
放入列表中。假设T
是Apple
。 this
可以比较两个苹果。List<T>
可以包含两个苹果。但是List<T>
不能包含两个苹果比较器,因为那不是它的类型!
其次,你不明白“协方差”这个词是什么意思。我不知道您认为这意味着什么,但我的猜测是您认为“协方差”意味着“分配兼容性”。这通常是人们认为“协方差”的意思,当他们对它的含义有误时。
“赋值兼容性”是 type 的值Apple
可以进入 type 的变量的属性Fruit
,因为 anApple
是与存储兼容的赋值Fruit
。 那不是协方差。
协变是泛型类型保留赋值兼容性关系的属性。
也就是说: an Apple
can 进入 type 的变量Fruit
,因此 an IEnumerable<Apple>
can 进入 type 的变量IEnumerable<Fruit>
。分配兼容性的关系被保留,因为类型通过使它们泛型而变化,因此IEnumerable<T>
是协变的;事物朝着同一个方向变化。
如果我声明
T
继承自B<T>
第一个 this 没有错误,但Add
this
剩下的错误。
首先:不要这样做。这是一个糟糕的模式。它是 C++ 模式的一种变体,称为“奇怪重复的模板模式”。我已经看到它在 C# 中使用了很多次,而且几乎总是用错了。躲开它。它使您的类型变得复杂、难以使用、难以理解,并且使您认为您对您的类型有一个 C# 不支持的约束。
现在可以肯定的是,如果
T
each 继承自B<T>
,那么 eachB<T>
就是 aT
,因此应该进入 aList<T>
。
现在你也许开始明白为什么这种模式如此糟糕了。它让你相信完全疯狂的虚假事物,因为它是如此令人困惑!
让我们再次让你的句子不那么混乱。我们将替换T
为Apple
和B<T>
并且Fruit
我们有:
现在可以肯定,如果每个苹果都是一种水果,那么每个水果都是一个苹果,因此应该放入一碗苹果中”。
你的结论是苹果是水果,因此所有的水果都是苹果,所以你可以把一根香蕉放进一碗苹果里,它仍然是一碗苹果。显然,这是荒谬的。
我究竟做错了什么?
您正在构建的泛型类型非常复杂,以至于您无法正确理解它们。这意味着使用您的代码的人也将无法理解它们。找到一种更简单的方法来解决您的问题。
推荐阅读
- android - Android WebView 从内部存储加载 | 安卓 11 | 安卓
- sql - 将数据从一个表连接到另一个表
- c - vfprintf 中的段错误,堆栈损坏
- javascript - 副作用可以包含在不纯计算和惰性的描述中吗?
- jenkins - Jenkins - 如何选择要部署到的 Kubernetes 服务器?
- angular - Angular 明确排除包
- javascript - 打字稿中不同类型的函数参数
- javascript - 角色分配向特定频道发布公告
- azure - 如何将 Blobfuse 升级到最新版本
- android - 在 Android Studio 中使用外部 cmake 可执行文件