Перечисление списка объектов в список против IList

Просто наткнулся на это:

Func<List<object>> foo = () => new List<object>();
List<string> s = (List<string>)foo();
IList<string> s1 = (IList<string>)foo();

Компилятор жалуется на листинг в List (имеет смысл), но ничего не говорит о IList. Заставляет меня задаться вопросом, почему?

Ответ 1

Компилятор знает, что List<X> не может быть List<Y>.
Поэтому он дает ошибку компилятора.

Однако второй прилив может быть успешным, если List<X> на самом деле является некоторым производным классом, который также реализует IList<Y>.

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

Чтобы процитировать спецификацию (§6.4.2)

Явные ссылочные преобразования:

  • От объекта и динамического до любого другого ссылочного типа.
  • От любого класса S до любого типа типа T, если S является базовым классом T.
  • От любого класса S до любого типа интерфейса T, если S не запечатан и не предоставляется S, не реализует T.
  • От любого типа интерфейса S до любого типа типа T, если T не запечатан или не предоставлен T, реализует S.
  • От любого типа интерфейса S до любого типа интерфейса T, если S не является производным от T.
  • [надрез]

(выделено курсивом)

В предложениях provided... исключаются фактически неявные преобразования.

Ответ 2

Объект, который известен как List<object>, может реализовать IList<string>, а также IList<object>, поэтому возможно, что приведение может быть успешным. В этом случае это невозможно, поскольку мы знаем, что оператор просто new List<object>(), но компилятор этого не считает. Возможно, вы расширили List<T> и внедрили другое, например

// not recommended, but second cast works
public class MyWeirdList : List<object>, IList<string>

Объект, который, как известно, является List<object>, также не может быть List<string>, потому что вы можете наследовать только один тип.

public class MyWeirdList : List<object>, List<string> // compiler error

Если List<T> были запечатаны, оба приведения недействительны, потому что тогда компилятор точно знал, что класс не сможет реализовать IList<string>. Вы можете попробовать это, используя этот класс:

public sealed class SealedList<T> : List<T> { }

Ответ 3

Первая строка выходит из строя во время компиляции, вторая - "Невозможно передать объект типа" System.Collections.Generic.List 1[System.Object]' to type 'System.Collections.Generic.IList 1 [System.String] '. " исключение во время выполнения.

Если вы посмотрите на этот вопрос (Cast IList <string> на IList <object> не удается во время выполнения), в ответ разъясняется, почему этот компилятор работает, а также предоставляет пример для класса, который мог бы удовлетворять условиям.