为什么IEnumerable不需要强制转换但是List呢?

时间:2013-09-05 23:41:03

标签: c#

给出以下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有什么不同允许它在没有明确请求的情况下进行演员?

我认为答案涉及Covariance这个词,但我对此并不了解,只能自己解决。

3 个答案:

答案 0 :(得分:4)

结帐this post about covariance。他提供了一个解释和一种方法,使代码可以在没有演员的情况下工作。列表不是协变的,但您可以像这样更改方法:

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真的是一个狗的名单,而不是动物。因此,向它添加Cat将是一个问题,特别是导致编译器错误。

答案 1 :(得分:3)

  

我想这个答案涉及Covariance这个词

是的,你是对的,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

答案 2 :(得分:0)

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

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

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