无法从List <Bar>转换为List <Foo>

use*_*702 42 .net c# generics inheritance

我有这样的设置:

abstract class Foo {}
class Bar : Foo {}
Run Code Online (Sandbox Code Playgroud)

和其他形式的方法:

void AddEntries(List<Foo>) {}
Run Code Online (Sandbox Code Playgroud)

我试图使用类型对象列表调用此方法 Bar

List<Bar> barList = new List<Bar>()
AddEntries(barList);
Run Code Online (Sandbox Code Playgroud)

但这给了我错误:

无法从List <Bar>转换为List <Foo>

反正这个问题呢?我需要使用抽象类来保持方法定义.

Mig*_*oom 65

您可以制作AddEntries通用名称并将其更改为此

void AddEntries<T>(List<T> test) where T : Foo
{
    //your logic 
}
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请查看类型参数约束.


Rob*_*Rob 60

从docs.microsoft.com中读取泛型中协方差和逆变,因此,特别是"使用协变类型参数的通用接口"部分将涵盖您正在使用的场景.

简而言之,如果您(可以)将AddEntries方法的签名更改为:

static void AddEntries(IEnumerable<Foo> entries)
Run Code Online (Sandbox Code Playgroud)

(注意使用IEnumerable<Foo>而不是List<Foo>)你将能够做你想做的事情.

这里的具体区别是IEnumerable<T>List<T>不同的声明:

public interface IEnumerable<out T> : IEnumerable
public class List<T> : IList<T>, ... ...
Run Code Online (Sandbox Code Playgroud)

该差重要的是out在声明的IEnumerable<T>,其指示它是协变,这意味着(与来自docs.microsoft.com采取的定义):

协方差:使您可以使用比最初指定的派生类型更多的派生类型.

  • @Taemyr:我认为`AddEntries`意味着"将这些条目添加到其他列表中",而不是"将一些条目添加到此列表中".否则这两个答案都不适用于他,因为无论如何他都不能将`FooChild`添加到`List <Bar>`. (7认同)

Zei*_*kki 43

由于一个简单的原因,这是不允许的.假设以下编译:

AddEntries(new List<Bar>());

void AddEntries(List<Foo> list)
{
   // list is a `List<Bar>` at run-time
   list.Add(new SomethingElseDerivingFromFoo()); // what ?!
}
Run Code Online (Sandbox Code Playgroud)

如果您的代码会编译,那么您添加SomethingElseDerivingFromFoo到实际的List<Bar>(运行时类型)中会有一个不安全的添加(这会使整个通用列表毫无意义).

要解决您的问题,您可以使用泛型:

void AddEntries<T>(List<T> list) where T:Foo
{

}
Run Code Online (Sandbox Code Playgroud)

  • 竖起大拇指解释*为什么*你不能进行演员表演,而不仅仅是展示一种让它发挥作用的方法. (3认同)