Связь между списком <> и открытым типом IEnumerable <>

Есть ли связь между открытыми типами List<> и IEnumerable<>?

Пример:

var type1 = typeof(List<>);
var type2 = typeof(IEnumerable<>);

//return false
type2.IsAssignableFrom(type1);

Есть ли способ проверить связь между двумя открытыми типами, или отношения существуют только для закрытых типов?

Ответ 1

Хотя Джон Ву написал хороший ответ о различиях между определением типа и фактическим типом, я не думаю, что он полностью отвечает на проблему/вопрос, заданный в ОП.

Есть ли способ проверить связь между двумя открытыми типами, или отношения существуют только для закрытых типов?

Прежде всего, отношения всегда существуют; каждый List<> всегда является IEnumerable<>, как вы можете видеть в определении типа List<T>:

public class List<T>: IList<T>, ICollection<T>, IEnumerable<T>,...

Но это не отвечает на ваш второй вопрос, если есть способ проверить, существует ли такая связь между двумя типами. Можно подумать, что метод IsAssignableFrom можно использовать для проверки существования взаимосвязи между типами открытых типов, но вы не можете. Зачем? Давайте узнаем в документации функции IsAssignableFrom:

Type.IsAssignableFrom(Type c) Возвращает true, если выполняется любое из следующих условий:

  1. c и текущий экземпляр представляют один и тот же тип.
  2. c выводится прямо или косвенно из текущего экземпляра. c получается непосредственно из текущего экземпляра, если он наследуется от текущего экземпляра; c получается косвенно из текущего экземпляра, если он наследуется от последовательности одного или нескольких классов, которые наследуются от текущего экземпляра.

  3. Текущий экземпляр - это интерфейс, который реализует c.

  4. c является параметром универсального типа, и текущий экземпляр представляет одно из ограничений c.

и false, если ни одно из этих условий не выполняется, или если c равно нулю.

В вашем случае ни одно из вышеупомянутых условий не приведет к истине, так как: (1) они не одного типа. (2) Они не могут быть получены друг от друга, поскольку они являются открытыми типами: их параметры универсального типа неизвестны (unknown != unknown). List<> реализует закрытый тип IEnumerable<T> а не открытый тип IEnumerable<> (3). Они не являются общими параметрами типа (4).

Чтобы выяснить, имеют ли два универсальных типа отношение, вам необходимо проверить их type definitions (и их вложенные интерфейсы/базовые типы) и проверить, что они имеют отношение:

public static bool IsAssignableToOpenGenericType(Type givenType, Type genericType)
{
    var interfaceTypes = givenType.GetInterfaces();

    foreach (var it in interfaceTypes)
    {
        if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
            return true;
    }

    if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
        return true;

    Type baseType = givenType.BaseType;
    if (baseType == null) return false;

    return IsAssignableToGenericType(baseType, genericType);
}

(источник)

Приведет к:

    var typ1 = typeof(List<>);
    var typ2 = typeof(IEnumerable<>);

    // true, List<> type definition contains an IEnumerable<>
    Console.WriteLine(IsAssignableToOpenGenericType(typ1, typ2));
    // false, IEnumerable<> type definition does not contain List<>
    Console.WriteLine(IsAssignableToOpenGenericType(typ2, typ1));

Ответ 2

List<> и IEnumerable<> не являются типами; они являются определениями типов. Таким образом, на самом деле не имеет смысла спрашивать, назначается ли одно другому. Ни один из них не может быть назначен на. Вы не можете объявить переменную List<> a = null, для example-- вы получите ошибку компиляции "Неожиданное использование несвязанного универсального имени".

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

Если вы имеете в виду определение типа и хотите проверить совместимость типов, просто используйте <object> (или подходящий тип, если есть ограничение типа) вместо <>:

var type1 = typeof(List<object>);
var type2 = typeof(IEnumerable<object>);

//returns true
type2.IsAssignableFrom(type1);