Comment déterminer si un type implémente une interface avec la réflexion C #

562

Est -ce que la réflexion en C#offre un moyen de déterminer si certains étant donné les System.Typemodèles de type une interface?

public interface IMyInterface {}

public class MyType : IMyInterface {}

// should yield 'true'
typeof(MyType)./* ????? */MODELS_INTERFACE(IMyInterface);
Yippie-Ki-Yay
la source

Réponses:

969

Vous avez quelques choix:

  1. typeof(IMyInterface).IsAssignableFrom(typeof(MyType))

  2. typeof(MyType).GetInterfaces().Contains(typeof(IMyInterface))

Pour une interface générique, c'est un peu différent.

typeof(MyType).GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMyInterface<>))
Jeff
la source
68
N'oubliez pas que typeof (IMyInterface) .IsAssignableFrom (typeof (IMyInterface)) est également vrai, ce qui peut avoir un résultat inattendu sur votre code.
Chris Kemp
29
C'était sûr qu'il était facile de ne pas faire attention et d'obtenir les arguments en IsAssignableFromarrière. J'irai avec GetInterfacesmaintenant: p
Benjamin
12
La IsAssignableFrom(t1)variante est environ 3 fois plus rapide que l' GetInterfaces().Contains(t2)homologue de mon code.
Pierre Arnaud
24
@PierreArnaud: IsAssignableFrom appelle finalement GetInterfaces, donc probablement votre test a vérifié les GetInterfaces en premier et IsAssignable après. C'est parce que GetInterfaces met en cache ses résultats, donc la première invocation coûte plus cher
Panos Theof
17
Un petit changement dans la réponse de @ Kosta. Avec C # 6, nous pouvons typeof(MyType).GetInterface(nameof(IMyInterface)) != nullaméliorer la sécurité des types et la refactorisation.
aholmes
74

Utilisation Type.IsAssignableFrom:

typeof(IMyInterface).IsAssignableFrom(typeof(MyType));
Snea
la source
32
typeof(IMyInterface).IsAssignableFrom(someclass.GetType());

ou

typeof(IMyInterface).IsAssignableFrom(typeof(MyType));
ajma
la source
34
Si vous avez déjà une instance de la classe, une bien meilleure approche est tout simplement someclass is IMyInterfacecar cela n'implique pas du tout le coût de la réflexion. Donc, même si ce n'est pas faux, ce n'est pas un moyen idéal de le faire.
James J.Regan IV
1
@James - D'accord. Même Resharper donne la même suggestion.
Angshuman Agarwal
@ JamesJ.ReganIV vous devriez poster cela comme réponse, j'ai failli manquer votre commentaire
reggaeguitar
@reggaeguitar, merci, mais le commentaire ne répond pas à la question d'origine. La question demande la solution de réflexion, je dis juste dans le premier cas de cette réponse où vous avez une instance de la réflexion d'objet n'est pas la solution idéale.
James J. Regan IV
1
@ JamesJ.ReganIV En fait, isvérifie dans les deux sens de la hiérarchie d'héritage alors que IsAssignableFromne vérifie que vers le haut. De plus, si vous avez une instance d'un objet, vous devez appeler IsInstanceOfType(qui ne regarde que vers le haut).
Sellorio
13
public static bool ImplementsInterface(this Type type, Type ifaceType) 
{
    Type[] intf = type.GetInterfaces();
    for(int i = 0; i < intf.Length; i++) 
    {
        if(intf[ i ] == ifaceType) 
        {
            return true;
        }
    }
    return false;
}

Je pense que c'est la version correcte, pour trois raisons:

  1. Il utilise GetInterfaces et non IsAssignableFrom, c'est plus rapide car IsAssignableFrom finit par appeler plusieurs fois GetInterfaces après plusieurs vérifications.
  2. Il itère sur le tableau local, il n'y aura donc pas de vérification des limites.
  3. Il utilise l'opérateur == qui est défini pour Type, il est donc probablement plus sûr que la méthode Equals (que l'appel Contains utilisera éventuellement).
Panos Theof
la source
10
+1 pour le contenu, je déteste les espaces autour des parens et des accolades égyptiennes. La méthode entière peut également s'écrire: return type.GetInterfaces (). Any (t => t == ifaceType);
reggaeguitar
1
Type.IsAssignableFrom () agit en interne exactement comme votre code
devi
1
Aussi pourquoi ne pas taper.GetInterfaces (). Contient (ifaceType) qui n'utilise pas LINQ.
9

Je viens de faire:

public static bool Implements<I>(this Type source) where I : class
{
  return typeof(I).IsAssignableFrom(source);
}

J'aurais aimé pouvoir dire where I : interface, mais ce interfacen'est pas une option de contrainte de paramètre générique. classest aussi proche que possible.

Usage:

if(MyType.Implements<IInitializable>())
  MyCollection.Initialize();

Je viens de le dire Implementsparce que c'est plus intuitif. Je suis toujours IsAssignableFromretourné.

toddmo
la source
Vous pouvez faire return typeof(I).IsInterface && typeof(I).IsAssignableFrom(source);pour retourner false sur toute utilisation «incorrecte» de la méthode, c'est-à-dire; en l'utilisant avec un type de classe au lieu d'un type d'interface, lève également une exception si le paramètre-type n'est pas une interface. Bien que vous puissiez affirmer qu'une classe dérivée «implémente» son parent ...
Sindri Jóelsson
7

Modifier la réponse de Jeff pour des performances optimales (grâce au test de performance de Pierre Arnaud):

var type = typeof(MyType);
var implementsInterface = typeof(IMyInterface).IsAssignableFrom(type) && type.IsClass;

Pour trouver tous les types qui implémentent une interface dans un domaine donné Assembly:

var implementations = typeof(TypeInTargetAssembly).Assembly.GetTypes()
                          .Where(t => typeof(IMyInterface).IsAssignableFrom(t) && t.IsClass);
Ben Wilde
la source
7

Comme quelqu'un d'autre l'a déjà mentionné: Benjamin 10 avril 13 à 22:21 "

Il était sûr qu'il était facile de ne pas faire attention et de récupérer les arguments pour IsAssignableFrom à l'envers. J'irai avec GetInterfaces maintenant: p -

Eh bien, une autre solution consiste simplement à créer une méthode d'extension courte qui remplit, dans une certaine mesure, la façon de penser "la plus habituelle" (et il a été convenu que c'est un très petit choix personnel pour le rendre légèrement "plus naturel" en fonction de ses préférences). ):

public static class TypeExtensions
{
    public static bool IsAssignableTo(this Type type, Type assignableType)
    {
        return assignableType.IsAssignableFrom(type);
    }
}

Et pourquoi ne pas aller un peu plus générique (eh bien je ne sais pas si c'est vraiment si intéressant, eh bien je suppose que je passe juste une autre pincée de sucre de 'syntaxe'):

public static class TypeExtensions
{
    public static bool IsAssignableTo(this Type type, Type assignableType)
    {
        return assignableType.IsAssignableFrom(type);
    }

    public static bool IsAssignableTo<TAssignable>(this Type type)
    {
        return IsAssignableTo(type, typeof(TAssignable));
    }
}

Je pense que cela pourrait être beaucoup plus naturel de cette façon, mais encore une fois, juste une question d'opinions très personnelles:

var isTrue = michelleType.IsAssignableTo<IMaBelle>();
Kerry Perret
la source
4
Y a-t-il une raison pour laquelle vous n'avez pas simplement mis l'implémentation directement dans la méthode d'extension? Je veux dire sûr que cela vous permet de l'appeler dans les deux sens, mais pourquoi auriez-vous besoin de faire cela?
Mark A. Donohoe
@MarqueIV désolé de vous répondre près de 2 ans de retard, eh bien je suppose que c'était une vieille mauvaise habitude à l'époque d'envelopper la méthode d'assistance dans la méthode d'extension pour éviter de répéter le code, modifiera ma réponse :)
Kerry Perret
1
@MarqueIV done plus a changé mon autre mauvaise habitude de ne pas utiliser d'alias, c'est-à-dire Boolean=> bool(je ne sais pas pourquoi j'avais l'habitude d'avoir des règles de codage "fantaisistes" strictes quand j'étais plus jeune).
Kerry Perret
3

Si vous avez un type ou une instance, vous pouvez facilement vérifier s'ils prennent en charge une interface spécifique.

Pour tester si un objet implémente une certaine interface:

if(myObject is IMyInterface) {
  // object myObject implements IMyInterface
}

Pour tester si un type implémente une certaine interface:

if(typeof(IMyInterface).IsAssignableFrom(typeof(MyType))) {
  // type MyType implements IMyInterface
}

Si vous avez un objet générique et que vous souhaitez effectuer un cast ainsi qu'une vérification si l'interface vers laquelle vous castez est implémentée, le code est:

 var myCastedObject = myObject as IMyInterface;

    if(myCastedObject != null) {
      // object myObject implements IMyInterface
    }

la source
2

IsAssignableFromest désormais déplacé vers TypeInfo:

typeof(ISMSRequest).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo());
ordinateur de code
la source
1

Quiconque le recherche peut trouver utile la méthode d'extension suivante:

public static class TypeExtensions
{
    public static bool ImplementsInterface(this Type type, Type @interface)
    {
        if (type == null)
        {
            throw new ArgumentNullException(nameof(type));
        }

        if (@interface == null)
        {
            throw new ArgumentNullException(nameof(@interface));
        }

        var interfaces = type.GetInterfaces();
        if (@interface.IsGenericTypeDefinition)
        {
            foreach (var item in interfaces)
            {
                if (item.IsConstructedGenericType && item.GetGenericTypeDefinition() == @interface)
                {
                    return true;
                }
            }
        }
        else
        {
            foreach (var item in interfaces)
            {
                if (item == @interface)
                {
                    return true;
                }
            }
        }

        return false;
    }
}

tests xunit:

public class TypeExtensionTests
{
    [Theory]
    [InlineData(typeof(string), typeof(IList<int>), false)]
    [InlineData(typeof(List<>), typeof(IList<int>), false)]
    [InlineData(typeof(List<>), typeof(IList<>), true)]
    [InlineData(typeof(List<int>), typeof(IList<>), true)]
    [InlineData(typeof(List<int>), typeof(IList<int>), true)]
    [InlineData(typeof(List<int>), typeof(IList<string>), false)]
    public void ValidateTypeImplementsInterface(Type type, Type @interface, bool expect)
    {
        var output = type.ImplementsInterface(@interface);
        Assert.Equal(expect, output);
    }
}
Bill Barry
la source
0

qu'en est-il de

if(MyType as IMyInterface != null)

?

Pingi
la source
4
C'est évident quand j'ai une instance. Inutile lorsque j'ai un type de réflexion
edc65
0

Qu'en est-il de

typeof(IWhatever).GetTypeInfo().IsInterface
LaWi
la source
0

Une bonne réponse est

typeof(MyType).GetInterface(nameof(IMyInterface)) != null;

cependant,

typeof(MyType).IsAssignableFrom(typeof(IMyInterface));

peut renvoyer un résultat incorrect, comme le montre le code suivant avec chaîne et IConvertible:

    static void TestIConvertible()
    {
        string test = "test";
        Type stringType = typeof(string); // or test.GetType();

        bool isConvertibleDirect = test is IConvertible;
        bool isConvertibleTypeAssignable = stringType.IsAssignableFrom(typeof(IConvertible));
        bool isConvertibleHasInterface = stringType.GetInterface(nameof(IConvertible)) != null;

        Console.WriteLine($"isConvertibleDirect: {isConvertibleDirect}");
        Console.WriteLine($"isConvertibleTypeAssignable: {isConvertibleTypeAssignable}");
        Console.WriteLine($"isConvertibleHasInterface: {isConvertibleHasInterface}");
    }

Résultats:

 isConvertibleDirect: True
 isConvertibleTypeAssignable: False
 isConvertibleHasInterface: True
EricBDev
la source
4
Comme vous pouvez le voir dans la réponse acceptée, vous avez échangé les types d'utilisation de IsAssignableFrom. Tout comme Benjamin et Ehouarn le préviennent.
VV5198722
0

Notez que si vous avez une interface générique, IMyInterface<T>cela retournera toujours false:

  typeof(IMyInterface<>).IsAssignableFrom(typeof(MyType)) /* ALWAYS FALSE */

Cela ne fonctionne pas non plus:

  typeof(MyType).GetInterfaces().Contains(typeof(IMyInterface<>))  /* ALWAYS FALSE */

Cependant, si MyTypeimplémente IMyInterface<MyType>cela fonctionne et retourne true:

  typeof(IMyInterface<MyType>).IsAssignableFrom(typeof(MyType))

Cependant, vous ne connaîtrez probablement pas le paramètre de type Tlors de l'exécution . Une solution quelque peu hacky est:

  typeof(MyType).GetInterfaces()
                .Any(x=>x.Name == typeof(IMyInterface<>).Name)

La solution de Jeff est un peu moins hacky:

  typeof(MyType).GetInterfaces()
         .Any(i => i.IsGenericType 
             && i.GetGenericTypeDefinition() == typeof(IMyInterface<>));

Voici une méthode d'extension Typequi fonctionne dans tous les cas:

public static class TypeExtensions
{
    public static bool IsImplementing(this Type type, Type someInterface)
    {
        return type.GetInterfaces()
             .Any(i => i == someInterface 
                 || i.IsGenericType 
                    && i.GetGenericTypeDefinition() == someInterface);
    }
}

(Notez que ce qui précède utilise linq, qui est probablement plus lent qu'une boucle.)

Vous pouvez alors faire:

   typeof(MyType).IsImplementing(IMyInterface<>)
Diego
la source