2009-06-04 13 views
15

Chcę napisać metodę wykorzystującą odbicie, aby stwierdzić, czy dany typ implementuje IList<T>. Na przykład:Jak stwierdzić, czy typ implementuje 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 

W moim użytkowania, mogę założyć, że typ zawsze będzie instancję typu rodzajowego (lub coś, co nie jest nazwą rodzajową w ogóle).

Niestety, nie jest to takie proste, jak powinno być. Oczywiste rozwiązanie:

public bool IsGenericList(Type type) 
{ 
    return typeof(IList<>).IsAssignableFrom(type); 
} 

nie działa; zawsze zwraca wartość false. Pozornie niestanowiące instancji typy ogólne, takie jak IList<>, nie implementują IsAssignableFrom w sposób, w jaki ich oczekuję: IList<> nie można przypisać z poziomu.

Próbowałem to również:

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

Tj skręcić type do jej wystąpienia non-generic, jak IList<int> ->IList<>, a następnie spróbuj ponownie IsAssignableFrom. Że zwróci true, gdy typ jest instancja IList<T> takich jak IList<int>, IList<object> itp ale zwraca false dla klas, które implementują IList<T> takich jak List<int>, ObservableCollection<double> itp, więc widocznie IList<> nie jest przypisane z List<>. Ponownie, nie to, co bym się spodziewał.

Jak mam napisać IsGenericList i jak działa to w powyższych przykładach?

Odpowiedz

23

W rzeczywistości nie można mieć wystąpienia ogólnej definicji typu. Dlatego metoda IsAssignableFrom() działa zgodnie z oczekiwaniami. Aby osiągnąć to, co chcesz, wykonaj następujące czynności:

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; 
} 

Po prostu z ciekawości, po co ci to potrzebne?

+1

+1 - Działa to i jest jedynym rozwiązaniem, które obecnie ma miejsce - po prostu zdałem sobie sprawę, że masz to po tym, jak napisałem to samo. –

+0

Nigdy nie widziałem @ do używania słów kluczowych jako nazw zmiennych w każdym kodzie produkcyjnym. – VVS

+0

Dobre rozwiązanie. :) Mały punkt jednak: używanie symboli @ nie jest ogólnie zalecane. (Zamiast tego możesz wywołać zmienną 'i' lub' curInterface', aby rozwiązać konflikt nazwy.) – Noldorin

-3

Użyj "is" operator:

operatora is służy do sprawdzania, czy typu run-time obiektu jest kompatybilny z danego typu.

+0

Nie mam instancji. Mam typ. –

+0

To nie zadziała z IList <>, tylko z IList , itp. Musisz mieć konkretny typ dla "jest" do pracy ... –

+0

Potrzebujesz instancji dla 'jest'. OP zapytał, jak wykonać test za pomocą "Typu". – Oliver

1

Lucero/Reed Copsey mają teraz odpowiednie rozwiązanie. Wystarczy, aby uczynić go bardziej zwięzłe, to jest tutaj w formie LINQified:

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

Nie będzie działać, ponieważ GetInterfaces() nie zwróci ogólnych definicji typów, tylko typy ogólne (takie jak IList ). – Lucero

+0

Testowany - nie działa (to była moja pierwsza próba). –

+0

Tak, więc nie ma. Zaktualizowałem go i wydaje się, że teraz działa. – Noldorin

1

Ten przekazuje swoje testy ...

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

nie musi być generyczny, aby zaimplementować ogólny interfejs. Możesz to zrobić: class IntList: object, IList {....} co nie powoduje generowania klasy IntList. – Lucero

+0

Kontrola IsGeneric w ogóle nie jest wymagana – tanascius

0

Czy próbowałeś dzwoniąc Type.GetInterface()? Z pomocy nie wynika jednoznacznie, ale myślę, że przeszuka interfejsy implementowane przez typy bazowe, oprócz samego typu. Jeśli nie, zawsze możesz przejść przez pętlę Type.BaseType i ponownie zadzwonić pod numer GetInterface().

+0

GetInterface działa tylko wtedy, gdy znasz konkretny argument ogólny, ale nie zadziała dla IList . –

2

Korzystanie następująco: http://msdn.microsoft.com/en-us/library/system.type.findinterfaces.aspx

Próbowałem to:

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

Używanie nazwy innej niż silna w celu identyfikacji typu jest bardzo niebezpieczne. Każdy może utworzyć typ o tej nazwie, nie oznacza to, że jest to typ, który chcesz przetestować. – Lucero

+0

Prawda, można po prostu zastąpić to: Typ [] x = type.FindInterfaces (myFilter, typeof (IList <>).FullName); – Irwin

11

ja też chcę przetestować jeśli typ implementuje IList<T> jakiegoś T. zrobiłem oczywistą zmianę do Lucero's answer ale spowodował subtle bug nieobecny w oryginalnej odpowiedzi. Oto moja ostatnia edycja:

+2

Dzięki, to jest rzeczywiście lepsze niż zaakceptowana odpowiedź, ponieważ działa dla 'IList ' sama. –

Powiązane problemy