我有2个课程A
和B
。 B
继承自A
。如果我有一个像这样的方法
void works(A aType)
我可以传递一个B
类型的对象。但是,void fails(List<A> aListType)
类似List<B>
类型的方法失败,因为Visual Studio抱怨它无法在List<A>
和List<B>
之间进行转换。我可以用泛型来解决这个问题,但为什么它首先会失败?另外,我应该只使用泛型吗?
这是一个基本的例子:
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
B bType1 = new B();
works(bType1); // This works
List<B> bListType1 = new List<B>();
works(bListType1); // This works
List<B> bListType2 = new List<B>();
fails(bListType2); // This fails
}
static void works(A aType)
{
return;
}
static void works<T>(List<T> aListType) where T : A
{
return;
}
static void fails(List<A> aListType)
{
return;
}
}
class A
{
}
class B : A
{
}
}
答案 0 :(得分:4)
因为List<T>
是C#中的不变类型,这意味着您只能使用最初在变量定义中指定的类型(即{{1} })。您会注意到,如果将A
替换为List<A>
,则您的错误就会消失。这是因为IEnumerable<A>
是协变,这意味着您可以使用比最初指定的更多派生类型(即IEnumerable<A>
)。详细了解Covariance and Contravariance in C# here。
答案 1 :(得分:3)
如果您将List<B>
传递给fails
,并且fails
某处的某个地方尝试向其添加A
(因为List<A>
已一个void Add(A item)
方法),你期望发生什么?
static void Fails(List<A> items)
{
aListType.Add(new A());
}
...
Fails(new List<B>());
让我们说这段代码编译;当它试图将A
添加到List<B>
时,它必须在运行时爆炸。这是一种类型安全的操作吗?
如果您只想List<B>
作为List<A>
迭代,则应使用IEnumerable<A>
界面。 List<B>
可以被视为IEnumerable<A>
,因为IEnumerable
界面是协变的。
static void AlsoWorks(IEnumerable<A> items)
{
foreach(var a in items)
{
Console.WriteLine("No problems here!");
}
}
...
AlsoWorks(new List<B>());
答案 2 :(得分:0)
Marc Gravell之前已经回答了这个问题。这里的解释非常适合您理解您所拥有的选项以及您尝试的原因。