Разве общий IList не может быть назначен из общего списка?

2
bool IsTypeAGenericList(Type listType)
{
  typeof(IList<>).IsAssignableFrom(listType.GetGenericTypeDefinition())
}

возвращает false, если задано typeof(List<int>).

Я предполагаю, что это связано с тем, что параметры двух типов могут быть разными, правильно?

Теги:
generics
collections

4 ответа

8
Лучший ответ

Собственно, это работает:

public static bool IsGenericList(Type type)
{
  if (!type.IsGenericType)
    return false;
  var genericArguments = type.GetGenericArguments();
  if (genericArguments.Length != 1)
    return false;

  var listType = typeof (IList<>).MakeGenericType(genericArguments);
  return listType.IsAssignableFrom(type);
}
  • 0
    Это работает в данном конкретном случае, но пропустит такие вещи, как MyIntList и т. Д., Как показано в ответе Джона. Я отправил другой ответ ниже, чтобы попытаться объяснить отношения.
3

Это действительно связано с открытыми построенными типами.

Когда вы говорите:

class List<T> : IList<T>

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

Он запутывается, потому что параметр типа IList<> также называется T - но это совсем другой параметр типа. Поэтому давайте повторно объявим наш конкретный класс следующим образом:

class List<U> : IList<U>

Это полностью эквивалентно вышесказанному, только теперь мы можем сказать "U", когда мы ссылаемся на параметр типа List и T, когда мы ссылаемся на один из IList. Они разные.

Теперь становится легче понять, почему определение универсального типа List<U> (что вы имеете в виду, когда говорите typeof(List<>)), не реализует определение типа genericc IList<T> (что вы имеете в виду, когда говорите typeof(IList<>)), а скорее реализует открытый родовой построенный тип IList<U> (то есть IList, построенный с помощью париметра типа List).

Таким образом, общие определения типов никогда не наследуют или не реализуют другие определения общего типа - они обычно реализуют открытые построенные типы, используя их собственные параметры типа с другими определениями общего типа.

Ответ Ripper234 показывает, как обрабатывать этот конкретный случай с помощью Reflection, поэтому я не буду его повторять; Я просто хотел уточнить отношения между этими типами, и я надеюсь, что это получилось, по крайней мере, несколько понятным.

2

Я думаю, что метод действительно не имеет смысла, потому что экземпляр никогда не относится к родовому типу - он всегда строится с определенным аргументом типа.

Другими словами, у вас никогда не может быть "открытой" переменной для назначения, а также ссылки на открытый экземпляр для использования в качестве значения для назначения.

Как вы говорите, вы не знаете, будут ли параметры типа одинаковыми - так (например) вы могли бы определить:

class BizarreList<T> : IList<int>

Похоже, что должен быть какой-то способ выражения отношений, хотя...

  • 0
    Эти отношения действительно могут быть выражены? Как вы сказали, никогда не будет экземпляра универсального типа, только экземпляры закрытого составного типа. Так может ли IList<> когда-либо назначаться из List<> ? Я говорю «нет», потому что это может быть правдой только в том случае, если оба типа были созданы с общим аргументом типа, и, как мы уже указывали, аргумент типа не будет иметься. Это очень интересная проблема, хотя :)
  • 0
    Было бы неплохо иметь возможность обнаружить, что List <T> реализует IList <T> для всех T, а не всегда реализует IList <string>. Я подозреваю, что это можно сделать с достаточными усилиями, но это было бы больно. По сути, API отражений не очень хорошо справляются с дженериками, IMO.
Показать ещё 3 комментария
0

Здесь метод расширения из AutoMapper:

    public static bool IsCollectionType(this Type type)
    {
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ICollection<>))
        {
            return true;
        }

        IEnumerable<Type> genericInterfaces = type.GetInterfaces().Where(t => t.IsGenericType);
        IEnumerable<Type> baseDefinitions = genericInterfaces.Select(t => t.GetGenericTypeDefinition());

        var isCollectionType = baseDefinitions.Any(t => t == typeof(ICollection<>));

        return isCollectionType;
    }

Ещё вопросы

Сообщество Overcoder
Наверх
Меню