5

给定以下 LinqPad 示例:

void Main()
{   
    List<DbItem> dbItems = new List<DbItem>
    {
        new DbItem{Id = 4, SomeValue = "Value For 4", OtherValue = 1},
        new DbItem{Id = 19, SomeValue = "Value For 19", OtherValue = 2}
    };

    ListMethod(dbItems.Cast<IDbItem>().ToList());   <-+
    EnumerableMethod(dbItems);                      <-+
}                                                     |  
                                                      |
//    These are the methods are I asking about -------+

public void ListMethod(List<IDbItem> dbItems)
{
    Console.WriteLine(dbItems);
}
public void EnumerableMethod(IEnumerable<IDbItem> dbItems)
{
    Console.WriteLine(dbItems);
}
public class DbItem : IDbItem
{
    public int Id {get; set;}
    public string SomeValue {get; set;}
    public int OtherValue {get; set;}
}   
public interface IDbItem 
{
    int Id {get; set;}
    string SomeValue {get; set;}
}

为什么需要先演员才能EnumerableMethod直接取名单?ListMethod

我知道从 DbItem 到 IDbItem 的转换正在发生,但是 IEnumerable 有什么不同,它允许它在没有明确请求的情况下进行转换?

我想答案涉及协方差这个词,但我对此知之甚少,无法自己弄清楚。

4

3 回答 3

4

查看这篇关于协方差的帖子。他给出了一个解释和一种在没有演员表的情况下使代码工作的方法。列表不是协变的,但您可以像这样更改方法:

public void ListMethod<T>(List<T> dbItems) where T : IDbItem
{
    Console.WriteLine(dbItems);
}

协变是将更派生的泛型类型分配给基本泛型类型的能力。原因List不是协变的,因为您可以这样做:

class Animal {}
class Dog : Animal {}
class Cat : Animal {}

void someFunction()
{
    List<Animal> dogs = new List<Dog>();
    dogs.Add(new Cat());
}

dogs确实是狗的列表,而不是动物的列表。因此添加 aCat将是一个问题,特别是导致编译器错误。

于 2013-09-05T23:56:14.227 回答
3

我想答案涉及协方差这个词

是的,你是对的,类型参数IEnumerable<T>是协变的。因此您可以使用您指定的类型或任何更衍生的类型

   public interface IEnumerable<out T> : IEnumerable

其中List<T>类型参数不是协变的

public class List<T> : IList<T>, ICollection<T>, 
    IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, 
    IEnumerable
于 2013-09-06T00:02:48.863 回答
0

因为您不能将 x 的集合转换为 y 的集合,即使 x 派生自 y,因此每个 x 都是 y。请参阅这个 SO 问题

这就是为什么将协变和逆变概念添加到语言中的原因。

遗憾List<T>的是不支持这些功能。

于 2013-09-05T23:59:44.150 回答