2009-06-04 2 views
15

Я хочу написать метод, который использует Reflection, чтобы указать, реализует ли данный тип IList<T>. Например:Как определить, реализует ли тип IList <>?

IsGenericList(typeof(int))      // should return false 
IsGenericList(typeof(ArrayList))     // should return false 
IsGenericList(typeof(IList<int>))    // should return true 
IsGenericList(typeof(List<int>))     // should return true 
IsGenericList(typeof(ObservableCollection<int>)) // should return true 

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

К сожалению, это не так просто, как должно быть. Очевидное решение:

не работает; он всегда возвращает false. По-видимому, неинтерпретированные общие типы, такие как IList<>, не реализуют IsAssignable. Оттого, что я ожидаю от них: IList<> не может быть назначен из List<T>.

Я также попытался это:

public bool IsGenericList(Type type) 
{ 
    if (!type.IsGenericType) 
     return false; 
    var genericTypeDefinition = type.GetGenericTypeDefinition(); 
    return typeof(List<>).IsAssignableFrom(genericTypeDefinition); 
} 

т.е. превратить type в его не-инстанцирован родовое, как IList<int> ->IList<>, а затем попробовать снова IsAssignableFrom. Это возвращает истину, если типа является экземпляр IList<T>, такими как IList<int>, IList<object> и т.д. Но это возвращает ложь для классов, которые реализуют IList<T> таких как List<int>, ObservableCollection<double> и т.д., поэтому очевидно IList<> не назначаемая из List<>. Опять же, не то, что я ожидал бы.

Как я могу написать IsGenericList и заставить его работать так же, как в приведенных выше примерах?

ответ

23

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

public bool IsGenericList(Type type) 
{ 
    if (type == null) { 
     throw new ArgumentNullException("type"); 
    } 
    foreach (Type @interface in type.GetInterfaces()) { 
     if (@interface.IsGenericType) { 
      if (@interface.GetGenericTypeDefinition() == typeof(ICollection<>)) { 
       // if needed, you can also return the type used as generic argument 
       return true; 
      } 
     } 
    } 
    return false; 
} 

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

+1

+1 - Это работает, и это единственное решение, которое сейчас существует - я только понял, что у вас это было после того, как я опубликовал то же самое. –

+0

Никогда не видел @ использовать ключевые слова как имена переменных в любом производственном коде. – VVS

+0

Хорошее решение. :) Небольшая точка, однако: использование символов @ обычно не рекомендуется. (Вы можете просто вызвать переменную 'i' или' curInterface' вместо этого, чтобы разрешить конфликт имен.) – Noldorin

-3

Используйте «is» оператор:

является оператор используется для проверки ли тип времени выполнения объекта совместим с данным типом.

+0

У меня нет экземпляра. У меня есть Тип. –

+0

Это не будет работать с IList <>, только с IList и т. Д. Вам нужно иметь конкретный тип для «is» для работы ... –

+0

Вам нужен экземпляр для 'is'. ОП попросил, как сделать проверку, используя «Тип». – Oliver

1

Lucero/Reed Copsey оба имеют правильное решение сейчас. Просто, чтобы сделать его более кратким, здесь в LINQified форме:

var isGenericList = type.GetInterfaces().Any(t => t.IsGenericType && 
    t.GetGenericTypeDefinition() == typeof(IList<>)); 
+0

Не будет работать, поскольку GetInterfaces() не будет возвращать общие определения типов, только общие типы (такие как IList ). – Lucero

+0

Протестировано - не работает (это была моя первая попытка тоже). –

+0

Да, так оно и есть. Я обновил его и, похоже, теперь работает. – Noldorin

1

Это проходит тесты ...

public static bool IsGenericList(Type type) 
{ 
    return type.Name == "IList`1" || type.GetInterface("IList`1") != null; 
} 
+0

Тип не обязательно должен быть общим для реализации общего интерфейса. У вас может быть это: класс IntList: object, IList {....}, который не генерирует IntList. – Lucero

+0

Проверка IsGeneric не обязательна вообще – tanascius

0

Вы пытались дозвониться Type.GetInterface()? Это не совсем понятно из справки, но я думаю, что он будет искать интерфейсы, реализуемые базовыми типами, в дополнение к самому типу. Если нет, вы всегда можете пройти через Type.BaseType и снова вызвать GetInterface().

+0

GetInterface работает только если вы знаете конкретный общий аргумент, но не будете работать для IList . –

2

Используя это: http://msdn.microsoft.com/en-us/library/system.type.findinterfaces.aspx

Я попытался это:

public class Test : IList<string> 
{ 
//implementation left out... 
} 

class Program 
    { 
     static void Main(string[] args) 
     { 
      Test t = new Test(); 
      TypeFilter myFilter = new TypeFilter(MyInterfaceFilter); 

      Type type = t.GetType(); 
      Type[] x = type.FindInterfaces(myFilter, "System.Collections.Generic.IList"); 
      Console.WriteLine(x.Length); 

     } 

     public static bool MyInterfaceFilter(Type typeObj, Object criteriaObj) 
     { 
      if (typeObj.ToString().Contains(criteriaObj.ToString())) 
       return true; 
      else 
       return false; 
     } 
    } 
+4

Использование ненастроенного имени для идентификации типа очень небезопасно. Любой может создать тип с этим именем, это не означает, что это тип, который вы хотите проверить. – Lucero

+0

Верно, вы могли бы просто заменить его на это: Тип [] x = type.FindInterfaces (myFilter, typeof (IList <>).Полное имя); – Irwin

11

Я тоже хочу, чтобы проверить, если тип реализует IList<T> для некоторых Т. я сделал очевидное изменение в Lucero's answer, но это вызвало subtle bug нет в первоначальном ответе. Вот мое окончательное изменение:

/// <summary> 
    /// Test if a type derives from IList of T, for any T. 
    /// </summary> 
    public bool TestIfGenericList(Type type) 
    { 
     if (type == null) 
     { 
      throw new ArgumentNullException("type"); 
     } 

     var interfaceTest = new Predicate<Type>(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>)); 

     return interfaceTest(type) || type.GetInterfaces().Any(i => interfaceTest(i)); 
    } 
+2

Спасибо, это действительно лучше, чем принятый ответ, поскольку он работает для 'IList ' сам. –

Смежные вопросы