2016-05-12 13 views
2

Ich muss testen, ob ein Wert eine Instanz einer generischen Basisklasse ist, ohne den generischen Typparameter zu kennen. Mit Hilfe der MSDN example als Basis meines Beispiel ist es das, was ich erreichen möchte:Ermitteln, ob das Objekt eine Instanz einer generischen Basisklasse ist, irgendein generischer Typ

using System; 

public class Class1<T> { } 
public class DerivedC1 : Class1<int> { } 

class IsSubclassTest 
{ 
    public static void Main() 
    { 
     Console.WriteLine(
      "DerivedC1 subclass of Class1: {0}", 
      typeof(DerivedC1).IsSubclassOf(typeof(Class1<>)) // <- Here. 
    ); 
    } 
} 

Während diese syntaktisch korrekt ist, ist es immer falsch ergibt. Wenn ich den generischen Typparameter entferne, funktioniert er wie erwartet (gibt true zurück).

Wie kann ich testen, ob ein Klassentyp eine Unterklasse einer generischen Basisklasse ist, ohne dessen generischen Typparameter zu kennen?

Antwort

3

Das Problem ist, dass DrevidedC1 ist nicht ein sublcass von Class1<T> es eine Unterklasse von Class1<int> ist. Stellen Sie sicher, dass Sie diesen subtilen Unterschied verstehen; Class1<T> ist ein offener Typ (T kann alles sein, es war nicht gesetzt worden ist), während DerivedC1 eine geschlossene erstreckt Class1<int> (es ist nicht mehr in T öffnen, T wird auf int und nur int). Also, wenn Sie wie folgt vorgehen:

typeof(DerivedC1).IsSubclassOf(typeof(Class1<>)) 

Die Antwort offenbar false ist.

Was Sie tun müssen, ist zu überprüfen, ob die generische Typdefinition von DerivedC1 ‚s Basistyp (man denke an sie als die entsprechenden offenen generischen Art von Class1<int>) gleich Class1<T>, die es eindeutig der Fall ist.

Der richtige Code lautet daher:

typeof(DerivedC1).BaseType.GetGenericTypeDefinition() == typeof(Class1<>)); 

Oder noch besser, als Matías Fidemraizer Staaten in seiner answer:

typeof(DerivedC1).BaseType.GetGenericTypeDefinition().IsAssignableFrom(typeof(Class1<>))); 
+0

@JohnWhite Wenn Ihre Prüfungen nicht mit Reflektion und Typen mit vollständigen Typnamen implementiert werden, sollten Sie die Typen von verschiedenen Baugruppen überprüfen, da Sie Ihre Prüfungen stark eingeben. –

+0

Nach langem Zögern habe ich Ihre Antwort gewählt, denn IMO bietet die beste Erklärung für das, was passiert, obwohl alle Antworten von höchster Qualität sind. Vielen Dank. –

2

Es gibt spezielle Methoden auf Type für diese Art von Sache. Soweit ich das sehen kann, müssen Sie Ihre Basistypen nach oben gehen und jedes Mal überprüfen, bis Sie entweder (a) eine Übereinstimmung treffen oder (b) an die Spitze der Vererbungshierarchie (d. H. System.Object) gelangen.

Als solche der folgenden (rekursiv) Erweiterungsmethode:

public static class TypeExtensions 
{ 
    public static bool IsDerivedFromGenericParent(this Type type, Type parentType) 
    { 
     if(!parentType.IsGenericType) 
     { 
      throw new ArgumentException("type must be generic", "parentType"); 
     } 
     if(type == null || type == typeof(object)) 
     { 
      return false; 
     } 
     if(type.IsGenericType && type.GetGenericTypeDefinition() == parentType) 
     { 
      return true; 
     } 
     return type.BaseType.IsDerivedFromGenericParent(parentType) 
      || type.GetInterfaces().Any(t=>t.IsDerivedFromGenericParent(parentType)); 
    } 
} 

ermöglicht es Ihnen, die folgenden

typeof(DerivedC1).IsDerivedFromGenericParent(typeof(Class1<>)) 

... und wird auch funktionieren, wenn du etwas testen abgeleitet von DerivedC1.

+0

@JohnWhite Könnte es sein, dass Sie eine Interface-Implementierung testen? Ich habe den obigen Code angepasst, um dies zu ermöglichen. Scheint gut zu funktionieren für eine Klasse, die von 'List <>' abgeleitet ist und testet, ob es 'IEnumerable <>' ist, die beide definitiv in einer anderen Assembly sind. – spender

2

Das Ändern typeof(DerivedC1).IsSubclassOf(typeof(Class1<>)) zu typeof(Class1<>).IsAssignableFrom(typeof(DerivedC1).BaseType.GetGenericTypeDefinition()) sollte in Ihrem Fall genug sein.

Type.IsAssignableFrom ist stärker als die Verwendung Type.IsSubClassOf, weil es nur überprüft, ob ein Typ zu anderen Typen zuweisbar ist. Dazu gehören des gleichen Typs, Schnittstellentypen und andere Fälle.

+0

Was meinst du mit _ "sollte in deinem Fall genug sein" _? Ich kämpfe darum, dass dies funktioniert. Könnte es sein, dass es nicht angemessen ist, wenn das Argument des generischen Typs ein Typ aus einer anderen Assembly ist? –

+0

@JohnWhite Nein, ich meine, wenn Sie es an einem einzigen Ort verwenden, sollten Sie die Lösung nicht verallgemeinern müssen. Sie können es verwenden wie es ist *; P –

+0

@ JohnWhite Ich würde mit der Route "IsAssignableFrom" gehen, es funktioniert auch, um Schnittstellentypen zu überprüfen. –

Verwandte Themen