我有这个代码示例。而且我不明白为什么它不能编译。它不允许从 D
转换至 A<B>
.
D
继承A<C>
. C
继承B
,所以这意味着我应该能够通过 D
进入A<B>
方法参数对吗?我可能在这里遗漏了一些明显的东西。
using System.Collections.Generic;
public class Program
{
public void Main()
{
var d = new D();
Foo(d); //Cannot convert D to A<B>
}
private void Foo(A<B> bar)
{
}
}
public abstract class A<TypeB> where TypeB : B
{
public abstract List<TypeB> list { get; set; }
}
public abstract class B
{
}
public class C : B
{
}
public class D : A<C>
{
public override List<C> list { get; set; }
}
最佳答案
此问题已在此站点上被问过数百次。
假设您有一碗水果。是一碗苹果吗?一个苹果是一种水果,那为什么一碗水果不是一碗苹果呢?显然,因为一碗水果可能已经包含香蕉,而一碗苹果则不是。
假设您有一碗苹果。是一碗水果吗?你想说“一个苹果是一种水果,因此一碗苹果就是一碗水果”。但这是错误的。 一碗水果是你可以放一根香蕉的东西,因为有一个你可以在一碗水果上做的操作你不能在一碗苹果上做,所以一碗苹果不能用于预期有一碗水果的语境。
您想要的那种转换的技术名称是“通用协变转换”。 C# 仅在少数特定情况下支持通用协变转换:
- “外部”类型必须是接口(interface)或委托(delegate),例如
IEnumerable<T>
. - “内部”类型必须是引用类型,如字符串,而不是值类型,如 int。
- “外部”类型必须标记为协变安全;这是由编译器检查的。
您可以使用 IEnumerable<Apple>
在 IEnumerable<Fruit>
的上下文中这是意料之中的,因为没有办法将香蕉放入 IEnumerable<Fruit>
中。但是你不能使用你的类 A<Apple>
作为A<Fruit>
因为编译器不相信您已经阻止了在只能接受 Apple 的方法中使用扩展 Fruit 的另一种 Banana 类型的所有可能性。
在此站点上搜索“协变和逆变”,您会发现很多关于此的问题。
关于c# - 方法 C# 的通用抽象参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45784081/