2009-02-02 7 views

Répondre

306

En utilisant le réponse de TcKs il peut également être fait avec la requête LINQ suivante:

bool isBar = foo.GetType().GetInterfaces().Any(x => 
    x.IsGenericType && 
    x.GetGenericTypeDefinition() == typeof(IBar<>)); 
+1

Ceci est une solution très élégante! Les autres que j'ai vu sur SO utilisent des boucles foreach ou des requêtes LINQ plus longues. Gardez à l'esprit que pour utiliser ceci, vous devez avoir .NET Framework 3.5. –

+7

Je recommande que vous en fassiez une méthode d'extension à la http://bit.ly/ccza8B - nettoiera tout cela très bien! –

+1

En fonction de vos besoins, vous devrez peut-être vous retrouver sur les interfaces renvoyées. –

4

Vous devez vérifier par rapport à un type construit de l'interface générique.

Vous devrez faire quelque chose comme ceci:

foo is IBar<String> 

parce IBar<String> représente ce type construit. La raison pour laquelle vous devez faire ceci est parce que si T est indéfini dans votre vérification, le compilateur ne sait pas si vous voulez dire IBar<Int32> ou IBar<SomethingElse>.

31

Vous devez monter dans l'arbre d'héritage et de trouver toutes les interfaces pour chaque classe dans l'arbre, et comparer typeof(IBar<>) avec le résultat de l'appel Type.GetGenericTypeDefinitionsi l'interface est générique. C'est un peu douloureux, certainement.

Voir this answer et these ones pour plus d'informations et de code.

+0

pourquoi ne pas simplement cast à IBar et vérifier null? (Je veux dire lancer avec 'comme' bien sûr) –

+3

T est inconnu et ne peut pas être casté à un type spécifique. – sduplooy

+0

@sduplooy: peut-être qu'il me manque quelque chose comment T peut être inconnu? il compilerait la classe publique Foo: IFoo {} –

19
public interface IFoo<T> : IBar<T> {} 
public class Foo : IFoo<Foo> {} 

var implementedInterfaces = typeof(Foo).GetInterfaces(); 
foreach(var interfaceType in implementedInterfaces) { 
    if (false == interfaceType.IsGeneric) { continue; } 
    var genericType = interfaceType.GetGenericTypeDefinition(); 
    if (genericType == typeof(IFoo<>)) { 
     // do something ! 
     break; 
    } 
} 
+1

Comme typeof (Foo) renvoie l'objet System.Type (décrivant Foo), l'appel GetType() retournera toujours le type pour System.Type. Vous devriez passer à typeof (Foo) .GetInterfaces() –

3

d'abord public class Foo : IFoo<T> {} ne compile pas parce que vous devez spécifier une classe au lieu de T, mais en supposant que vous faites quelque chose comme public class Foo : IFoo<SomeClass> {}

alors si vous faites

Foo f = new Foo(); 
IBar<SomeClass> b = f as IBar<SomeClass>; 

if(b != null) //derives from IBar<> 
    Blabla(); 
9

En tant que méthode d'aide l'extension

public static bool Implements<I>(this Type type, I @interface) where I : class 
{ 
    if(((@interface as Type)==null) || !(@interface as Type).IsInterface) 
     throw new ArgumentException("Only interfaces can be 'implemented'."); 

    return (@interface as Type).IsAssignableFrom(type); 
} 

Exemple d'utilisation:

var testObject = new Dictionary<int, object>(); 
result = testObject.GetType().Implements(typeof(IDictionary<int, object>)); // true! 
+2

"IsAssignableFrom" était exactement ce que je cherchais - merci – Jesper

+14

Cela ne veut pas travailler pour l'exigence du demandeur de ne pas connaître le paramètre de type générique. De votre exemple testObject.GetType(). Implements (typeof (IDictionary <,>)); retournera faux. – ctusch

4

J'utilise une version légèrement plus simple de @GenericProgrammers méthode d'extension:

public static bool Implements<TInterface>(this Type type) where TInterface : class { 
    var interfaceType = typeof(TInterface); 

    if (!interfaceType.IsInterface) 
     throw new InvalidOperationException("Only interfaces can be implemented."); 

    return (interfaceType.IsAssignableFrom(type)); 
} 

Utilisation:

if (!featureType.Implements<IFeature>()) 
     throw new InvalidCastException(); 
+3

Cela ne fonctionne toujours pas selon l'exigence de la question d'origine qui est pour les interfaces génériques. – nathanchere

0

Il ne devrait pas être quelque chose de mal ce qui suit:

Pour le crédit supplémentaire que vous pourriez attraper AmbiguousMatchException si vous vouliez fournir un type paramètre générique spécifique avec votre requête IBar.

+0

Eh bien, il est généralement préférable d'éviter d'utiliser des littéraux de chaîne lorsque cela est possible. Cette approche rendrait plus difficile le refactorisation de l'application, car le fait de renommer l'interface IBar ne changerait pas la chaîne littérale, et l'erreur ne serait détectable qu'à l'exécution. – andyroschy

+0

Autant que je suis d'habitude d'accord avec le commentaire ci-dessus sur l'utilisation de «chaînes magiques» etc, c'est toujours la meilleure approche que j'ai trouvée. Bien assez proche - testant le PropertyType.Name égal à "IWhatever'1". – nathanchere

+0

Pourquoi pas ça? 'bool implementsGeneric = (unObject.Implements (typeof (IBar <>). Nom)! = null);' –

3

Pour attaquer complètement le système de types, je pense que vous devez gérer la récursivité, par ex. IList<T>: ICollection<T>: IEnumerable<T>, sans lequel vous ne sauriez pas que IList<int> implémente finalement IEnumerable<>.

/// <summary>Determines whether a type, like IList&lt;int&gt;, implements an open generic interface, like 
    /// IEnumerable&lt;&gt;. Note that this only checks against *interfaces*.</summary> 
    /// <param name="candidateType">The type to check.</param> 
    /// <param name="openGenericInterfaceType">The open generic type which it may impelement</param> 
    /// <returns>Whether the candidate type implements the open interface.</returns> 
    public static bool ImplementsOpenGenericInterface(this Type candidateType, Type openGenericInterfaceType) 
    { 
     Contract.Requires(candidateType != null); 
     Contract.Requires(openGenericInterfaceType != null); 

     return 
      candidateType.Equals(openGenericInterfaceType) || 
      (candidateType.IsGenericType && candidateType.GetGenericTypeDefinition().Equals(openGenericInterfaceType)) || 
      candidateType.GetInterfaces().Any(i => i.IsGenericType && i.ImplementsOpenGenericInterface(openGenericInterfaceType)); 

    } 
1

Si vous voulez une méthode d'extension qui soutiendrait les types de base génériques, ainsi que des interfaces, j'ai développé la réponse de sduplooy:

public static bool InheritsFrom(this Type t1, Type t2) 
    { 
     if (null == t1 || null == t2) 
      return false; 

     if (null != t1.BaseType && 
      t1.BaseType.IsGenericType && 
      t1.BaseType.GetGenericTypeDefinition() == t2) 
     { 
      return true; 
     } 

     if (InheritsFrom(t1.BaseType, t2)) 
      return true; 

     return 
      (t2.IsAssignableFrom(t1) && t1 != t2) 
      || 
      t1.GetInterfaces().Any(x => 
       x.IsGenericType && 
       x.GetGenericTypeDefinition() == t2); 
    } 
1

Méthode pour vérifier si le type hérite ou implémente un générique tapez:

public static bool IsTheGenericType(this Type candidateType, Type genericType) 
    { 
     return 
      candidateType != null && genericType != null && 
      (candidateType.IsGenericType && candidateType.GetGenericTypeDefinition() == genericType || 
      candidateType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == genericType) || 
      candidateType.BaseType != null && candidateType.BaseType.IsTheGenericType(genericType)); 
    } 
Questions connexes