Если у вас есть интерфейс IFoo
и класс Bar : IFoo
, почему вы можете сделать следующее:
List<IFoo> foo = new List<IFoo>();
foo.Add(new Bar());
Но вы не можете сделать:
List<IFoo> foo = new List<Bar>();
Если у вас есть интерфейс IFoo
и класс Bar : IFoo
, почему вы можете сделать следующее:
List<IFoo> foo = new List<IFoo>();
foo.Add(new Bar());
Но вы не можете сделать:
List<IFoo> foo = new List<Bar>();
На случайном взгляде кажется, что это должно (как в пиве быть свободным) работать. Тем не менее, быстрый анализ здравомыслия показывает нам, почему он не может. Имейте в виду, что следующий код не будет компилировать. Он намеревался показать, почему это не разрешено, хотя оно выглядит в порядке до точки.
public interface IFoo { }
public class Bar : IFoo { }
public class Zed : IFoo { }
//.....
List<IFoo> myList = new List<Bar>(); // makes sense so far
myList.Add(new Bar()); // OK, since Bar implements IFoo
myList.Add(new Zed()); // aaah! Now we see why.
//.....
myList
- это List<IFoo>
, то есть он может принимать любой экземпляр IFoo
. Однако это противоречит тому, что он был создан как List<Bar>
. Поскольку наличие List<IFoo>
означает, что я мог бы добавить новый экземпляр Zed
, мы не можем этого допускать, так как базовый список на самом деле List<Bar>
, который не может разместить a Zed
.
Причина в том, что С# не поддерживает совместную и контравариантность для дженериков в версиях С# 3.0 или более ранних версий. Это выполняется в С# 4.0, поэтому вы сможете сделать следующее:
IEnumerable<IFoo> foo = new List<Bar>();
Обратите внимание, что в С# 4.0 вы можете использовать для IEnumerable <IFoo> , но вы не сможете использовать List <IFoo> . Причина связана с безопасностью типа, если вы можете использовать List <Bar> для списка <IFoo> вы могли бы добавить в список других разработчиков IFoo, не нарушая безопасность.
Для получения дополнительной информации о ковариации и контравариантности в С# у Эрика Липперта есть хорошая серия блога.
Если вам нужно преобразовать список в список базового класса или интерфейса, вы можете сделать это:
using System.Linq;
---
List<Bar> bar = new List<Bar>();
bar.add(new Bar());
List<IFoo> foo = bar.OfType<IFoo>().ToList<IFoo>();
Это связано с созданием списка, вы указали, что T является IFoo, поэтому вы не можете создавать его как панель, поскольку они разные, хотя Bar поддерживает IFoo.
Список - это тип в этом случае, и это не список вопросов наследования <IFoo> действительно отличается от List <Bar> . List не знает anythign или наследует характеристики IFoo или Bar.
Надеюсь, что это поможет.
Поскольку список IFoo
может содержать и некоторый Bar
, но список IFoo
- это не то же самое, что список Bar
s.
Обратите внимание, что вместо использования С# я использовал английский выше. Хочу подчеркнуть, что это не глубокая проблема; вы просто путаетесь с деталями синтаксиса. Чтобы понять ответ, вам нужно увидеть вне синтаксиса и подумать о том, что он на самом деле означает.
Список IFoo
может содержать Bar
, потому что a Bar
является IFoo
. Здесь мы говорим об элементах списка. Список по-прежнему является списком IFoo
s. Мы не изменили это.
Теперь список, который вы назвали foo
, по-прежнему является списком IFoo
(более педантично, foo
объявлен как List<IFoo>
). Это не может быть ничего. В частности, он не может быть внесен в список Bar
(List<Bar>
). Список Bar
- это совершенно другой объект, чем список IFoo
s.
Я использовал некоторый linq, чтобы упростить преобразование
List<Bar> bar = new List<Bar>();
bar.add(new Bar());
List<IFoo> foo = bar.Select(x => (IFoo)x).ToList();
Это немного меньше, чем другие ответы, но хорошо работает
List<Bar>
не наследуется от List<IFoo>
Если у вас есть список типов List<IFoo>
, вы можете вызвать list.add(new Baz());
, предполагая Baz
реализует IFoo
. Однако вы не можете сделать это с помощью List<Bar>
, поэтому вы не можете использовать List<Bar>
везде, где вы можете использовать List<IFoo>
.
Однако, поскольку Bar
реализует IFoo
, вы можете использовать панель везде, где вы используете IFoo
, поэтому передача строки для добавления работает, когда она ожидает, и IFoo
.