кодирование интерфейса, в списке - синтаксис - PullRequest
1 голос
/ 03 апреля 2012

Я извлек некоторые из моих конкретных классов в интерфейсы

Раньше у меня был класс с именем 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 ]

10 голосов
/ 03 апреля 2012

Никто на самом деле не сказал причину , почему это не работает.Предположим, что 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 # знает, что это может произойти, поэтому запрещает это.К сожалению, это не запрещает использовать для массивов :

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

Это форма небезопасной ковариации .Мы решили не допустить того же опасного шаблона для интерфейсов;интерфейсы могут быть ковариантными, только если компилятор может доказать, что такая ошибка невозможна.

5 голосов
/ 03 апреля 2012

К сожалению, параметры универсального типа не соответствуют тем же правилам набора типов, что и автономные типы.Они ограничены тем, что говорит универсальный тип, что позволяет;это называется ковариация и контравариантность , и в C # только массивы, интерфейсы и делегаты могут быть ковариантными или контравариантными.Конкретные типы, такие как List, не могут быть (по крайней мере, в C # 4.0).

(Причина, по которой генерики не работают так, как вы думаете в общем , заключается в том, что их невозможно узнатьчто универсальный тип делает с его параметрами типа: ковариация интуитивно понятна, потому что так работают простые присваивания, но во многих случаях нам действительно нужна контрвариантность: поскольку компилятор не может принять решение за нас, он по умолчанию не принимает ни того, ни другого, если вы не скажете иначе.)

Для получения дополнительной информации о со / контравариантности в C # 4 я бы порекомендовал вам ознакомиться с серией сообщений Эрика Липперта об этом, в частности:

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;     
}
1 голос
/ 03 апреля 2012

Это не то же самое, что

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());
0 голосов
/ 03 апреля 2012

Нет. 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 }))

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...