【问题标题】:Cannot convert from List<Bar> to List<Foo>无法从 List<Bar> 转换为 List<Foo>
【发布时间】:2017-07-27 11:31:35
【问题描述】:

我有这样的设置:

abstract class Foo {}
class Bar : Foo {}

以及这种形式的其他方法:

void AddEntries(List<Foo>) {}

我正在尝试使用 Bar 类型的对象列表调用此方法

List<Bar> barList = new List<Bar>()
AddEntries(barList);

但这给了我错误:

无法从 List 转换为 List

有没有解决这个问题?我需要使用抽象类保留方法定义。

【问题讨论】:

  • C# 是否像 Java 一样具有 List&lt;? extends Foo&gt; 的语法?
  • @dbush 是的,C# 的版本在接受的答案中
  • @harrichael 虽然与 Java 不同,C# 支持泛型协变,因此有更好的选择(参见 Rob 的回答)
  • 我实际上怀疑 Rob 的答案更好,因为我打赌 OP 是将传递的条目添加到聚合列表中,而不是改变参数。

标签: c# .net generics inheritance


【解决方案1】:

您可以将 AddEntries 设为通用并将其更改为此

void AddEntries<T>(List<T> test) where T : Foo
{
    //your logic 
}

查看Constraints on Type Parameters 了解更多信息。

【讨论】:

    【解决方案2】:

    阅读Covariance and Contravariance in Generics from docs.microsoft.com,尤其是“具有协变类型参数的通用接口”部分将涵盖您正在使用的场景。

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

    static void AddEntries(IEnumerable<Foo> entries)
    

    (注意使用IEnumerable&lt;Foo&gt; 而不是List&lt;Foo&gt;)您将能够做您想做的事情。

    这里的具体区别在于IEnumerable&lt;T&gt;List&lt;T&gt;的声明方式不同:

    public interface IEnumerable<out T> : IEnumerable
    public class List<T> : IList<T>, ... ...
    

    重要的区别在于outIEnumerable&lt;T&gt;的声明中表示它是协变的,这意味着(定义取自docs.microsoft .com):

    协方差:使您能够使用比最初指定的更多派生类型。

    【讨论】:

    • 考虑到方法名是AddEntries,OP不太可能使用IEnumerable。
    • @Taemyr 但是,OP 可以定义一个interface
    • @Taemyr:我假设AddEntries 的意思是“将这些条目添加到其他列表”,而不是“将一些条目添加到此列表”。否则,这两个答案都不适合他,因为无论如何他都无法将FooChild 添加到List&lt;Bar&gt;
    • 我只是补充一下,因为我永远记不起哪个是哪个(并且阅读该定义没有帮助),用于 co/contra 方差的 C# 语法非常棒,因为它允许您表达你的方法是否会接收类型为 in 的东西,或者它们是否会将它 out 传递出去。如果您接受它们,则可以采用子类型(因此可以转换为更派生的类型);如果您将它们 out 传递,则始终可以将其强制转换为派生较少的超类型(列表采用类型 inout,这就是为什么它必须是不变的)。清晰表达意图的关键字的绝佳选择。
    • @BlueRaja-DannyPflughoeft,这也是我的想法 =)
    【解决方案3】:

    这是不允许的,原因很简单。假设以下编译:

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

    如果您的代码可以编译,那么您添加了一个不安全的添加项(这使得整个通用列表毫无意义),您将SomethingElseDerivingFromFoo 添加到实际上是一个List&lt;Bar&gt;(运行时类型)。

    为了解决你的问题,你可以使用泛型:

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

    【讨论】:

    • 感谢您解释为什么您不能进行演员阵容,而不仅仅是展示使其发挥作用的方法。
    • 我不明白。这是perfectly valid piece of code。您不能依赖派生类中提供的任何附加功能(没有显式转换)。
    • @Phylogenesis 您不能将List&lt;Bar&gt; 传递给AddEntries,因为如果您将SomethingElseDerivingFromFoo 添加到该列表中,这将是一种奇怪的行为(您在@ 中有一个SomethingElseDerivingFromFoo 项目987654330@.以上代码我贴出来完美编译。
    • 哦,好的。修改后,您的解释更有意义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-12
    • 1970-01-01
    • 2013-06-02
    相关资源
    最近更新 更多