编码到接口,在列表中 - 语法

时间:2012-04-03 14:43:11

标签: linq c#-4.0 interface

我已经将一些具体的类提取到接口

我曾经有一个名为City的类,它实现了接口ICity

现在我尝试执行以下操作

public List<ICity> Cities { get; private set; }

var efCities = (from c in myentity.Cities  
                  orderby c.CityName
                  select c);

Cities = (efCities.Select(o => new City() { Id = o.Id, Country = o.Country, 
          Province = o.Province, CityName = o.CityName }).ToList());

我收到了以下内容:

  

无法隐式转换类型'System.Collections.Generic.List<City>'   到'System.Collections.Generic.List<ICity>'

据我所知,由于City实现ICity,我应该没事,不是吗? 不是我正在做的事情和去的一样:

ICity c = new City();

4 个答案:

答案 0 :(得分:10)

没有人真的说原因为什么这不起作用。假设Apple和Orange都实施了IFruit:

List<Orange> oranges = new List<Orange>();
List<IFruit> fruits = oranges; // You are trying to do this, which is illegal.
                               // Suppose it were legal. Then you could do this:
fruits.Add(new Apple());

因为您可以将苹果添加到水果列表中,但该列表实际上是橙色列表!你只需将苹果放入橙子列表中,而苹果不是橙子。

C#编译器知道这可能发生,所以它不允许它。不幸的是,它并不禁止 arrays

Orange[] oranges = new Orange[1];
IFruit[] fruits = oranges; // dangerous, but legal!
fruits[0] = new Apple(); // legal at compile time, crashes at runtime.

这是不安全协方差的一种形式。我们决定不允许接口使用相同的危险模式;如果编译器可以证明这样的错误是不可能的,那么接口只能是协变的。

答案 1 :(得分:5)

不幸的是,泛型类型参数不遵循与独立类型相同的类型转换规则。它们受到通用类型所允许的限制;这称为协方差逆变,在C#中,只有数组,接口和委托可以是协变的或逆变的。像List这样的具体类型不能(至少从C#4.0开始)。

(泛型不能像你想象的那样工作一般的原因是因为它无法知道泛型类型对其类型参数的作用;协方差是直观的,因为这是简单的赋值工作方式,但在许多情况下我们真正想要的是逆变;因为编译器无法为我们做出决定,除非你另有说明,否则默认为默认。)

有关C#4中共同/逆转的更多信息,我建议您查看Eric Lippert关于它的一系列帖子,特别是:

http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx

以及关于它的MSDN文章:

http://msdn.microsoft.com/en-us/library/dd799517.aspx

幸运的是,在这种情况下,有一个简单的答案,即明确的IEnumerable.Cast方法:

Cities = (efCities.Select(o => new City() { Id = o.Id, Country = o.Country, 
          Province = o.Province, CityName = o.CityName }).Cast<ICity>.ToList());

另一种选择是使用IEnumerable<T>代替List<T>IEnumerable<T>中的T 协变,因此您的作业可行:

interface IA
{
  int Foo();
}

class A : IA
{
  public int Foo()
  {
    return 0;
  }
}

public DoStuff()
{
  List<A> la = new List<A> { new A(), new A(), new A(), new A() };

  // This is an error -- List<A> is not covariant with List<IA>
  // List<IA> lia = la;  

  // This is fine; List<A> implements IEnumerable<A>
  IEnumerable<A> iea = la; 

  // Also fine: IEnumerable<A> is covariant with IEnumerable<IA>
  IEnumerable<IA> ieia = la;     
}

答案 2 :(得分:1)

不同
ICity c = new City();

List<ICity>List<City>属于自己的类型,List<City>不是来自List<ICity>

为选择添加强制转换可以解决问题:

Cities = (efCities.Select(o => (ICity)(new City() { Id = o.Id, Country = o.Country, 
          Province = o.Province, CityName = o.CityName })).ToList());

答案 3 :(得分:0)

没有。 List<City>List<ICity>.不同,而不是分配select.toList();到城市尝试类似的事情:

Cities.AddRange((efCities.Select(o => new City() { Id = o.Id, Country = o.Country, Province = o.Province, CityName = o.CityName }))