Come determinare se un tipo implementa un'interfaccia con la riflessione C #


562

Ha riflessione in C#offerta modo in cui una per determinare se alcuni dati System.Typemodelli tipo qualche interfaccia?

public interface IMyInterface {}

public class MyType : IMyInterface {}

// should yield 'true'
typeof(MyType)./* ????? */MODELS_INTERFACE(IMyInterface);

Risposte:


969

Hai alcune scelte:

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

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

Per un'interfaccia generica, è un po 'diverso.

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

68
Ricorda che typeof (IMyInterface) .IsAssignableFrom (typeof (IMyInterface)) è anche true, il che potrebbe avere un risultato imprevisto sul tuo codice.
Chris Kemp,

29
Di certo è stato facile non prestare attenzione e ottenere gli argomenti a IsAssignableFromritroso. Ora vado con GetInterfaces: p
Benjamin

12
La IsAssignableFrom(t1)variante è circa 3 volte più veloce della GetInterfaces().Contains(t2)controparte nel mio codice.
Pierre Arnaud,

24
@PierreArnaud: IsAssignableFrom alla fine chiama GetInterfaces, quindi probabilmente il test ha verificato prima GetInterfaces e IsAssignable dopo. Questo perché GetInterfaces memorizza nella cache i suoi risultati, quindi la prima invocazione costa di più
Panos Theof,

17
Una piccola modifica alla risposta di @ Kosta. Con C # 6 possiamo fare typeof(MyType).GetInterface(nameof(IMyInterface)) != nullper migliorare la sicurezza e il refactoring del tipo.
aholmes,


32
typeof(IMyInterface).IsAssignableFrom(someclass.GetType());

o

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

34
Se hai già un'istanza della classe, un approccio molto migliore è semplicemente someclass is IMyInterfaceperché ciò non comporta affatto il costo della riflessione. Quindi, sebbene non sia sbagliato, non è un modo ideale per farlo.
James J. Regan IV,

1
@James - Accetto. Anche Resharper dà lo stesso suggerimento.
Angshuman Agarwal,

@ JamesJ.ReganIV dovresti postarlo come risposta, ho quasi perso il tuo commento
reggaeguitar,

@reggaeguitar, grazie, ma il commento non risponde alla domanda originale. La domanda richiede la soluzione Reflection, sto solo dicendo nel primo caso di questa risposta in cui hai un'istanza dell'oggetto reflection non è la soluzione ideale.
James J. Regan IV,

1
@ JamesJ.ReganIV In realtà, iscontrolla in entrambe le direzioni della gerarchia ereditaria mentre IsAssignableFromcontrolla solo verso l'alto. Inoltre, se hai un'istanza di un oggetto, dovresti chiamare IsInstanceOfType(che guarda anche solo verso l'alto).
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;
}

Penso che questa sia la versione corretta, per tre motivi:

  1. Utilizza GetInterfaces e non IsAssignableFrom, è più veloce poiché IsAssignableFrom alla fine, dopo diversi controlli, chiama GetInterfaces.
  2. Iterate sull'array locale, quindi non ci saranno controlli sui limiti.
  3. Utilizza l'operatore == che è definito per Tipo, quindi probabilmente è più sicuro del metodo Equals (che alla fine utilizzerà la chiamata Contains).

10
+1 per il contenuto, odio gli spazi intorno alle parentesi e alle parentesi graffe egiziane. Inoltre, l'intero metodo può essere scritto come: return type.GetInterfaces (). Any (t => t == ifaceType);
reggaeguitar,

1
Type.IsAssignableFrom () si comporta esattamente come il tuo codice
devi il

1
Inoltre, perché non digitare.GetInterfaces (). Contiene (ifaceType) che non usa LINQ.

9

Ho appena fatto:

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

Vorrei poterlo dire where I : interface, ma interfacenon è un'opzione di vincolo di parametro generico. classè il più vicino possibile.

Uso:

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

Ho appena detto Implementsperché è più intuitivo. Mi viene sempre il IsAssignableFromflip-flop.


Si potrebbe fare return typeof(I).IsInterface && typeof(I).IsAssignableFrom(source);per restituire false su qualsiasi utilizzo "errato" del metodo, ovvero; usandolo con un tipo di classe anziché un tipo di interfaccia, in alternativa genera un'eccezione se il parametro type non è un'interfaccia. Sebbene si possa sostenere che una classe derivata "implementa" è genitore ...
Sindri Jóelsson,

7

Modifica della risposta di Jeff per prestazioni ottimali (grazie al test delle prestazioni di Pierre Arnaud):

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

Per trovare tutti i tipi che implementano un'interfaccia in un dato Assembly:

var implementations = typeof(TypeInTargetAssembly).Assembly.GetTypes()
                          .Where(t => typeof(IMyInterface).IsAssignableFrom(t) && t.IsClass);

7

Come qualcun altro ha già detto: Benjamin 10 aprile 13 alle 22:21 "

Di certo è stato facile non prestare attenzione e ottenere gli argomenti per IsAssignableFrom all'indietro. Ora vado con GetInterfaces: p -

Bene, un altro modo per aggirare è solo quello di creare un metodo di estensione breve che soddisfi, in una certa misura, il modo di pensare "più usuale" (e concordato che questa è una scelta molto piccola personale per renderlo leggermente "più naturale" in base alle proprie preferenze ):

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

E perché non andare un po 'più generico (beh, non sono sicuro che sia davvero così interessante, beh, suppongo che sto solo passando un altro pizzico di zucchero' sintattico '):

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

Penso che potrebbe essere molto più naturale in quel modo, ma ancora una volta solo una questione di opinioni molto personali:

var isTrue = michelleType.IsAssignableTo<IMaBelle>();

4
C'è un motivo per cui non hai semplicemente messo l'implementazione direttamente nel metodo di estensione? Voglio dire, questo ti consente di chiamarlo in entrambi i modi, ma perché mai dovresti farlo?
Mark A. Donohoe,

@MarqueIV mi dispiace di ricontattarti con quasi 2 anni di ritardo, beh, immagino fosse una vecchia cattiva abitudine allora avvolgere il metodo helper nel metodo di estensione per evitare la ripetizione del codice, modificherà la mia risposta :)
Kerry Perret il

1
@MarqueIV done plus ha cambiato la mia altra cattiva abitudine di non usare l'alias, ovvero Boolean=> bool(non ho mai avuto delle rigide regole "fantasiose" di programmazione quando ero più giovane).
Kerry Perret,

3

Se si dispone di un tipo o di un'istanza, è possibile verificare facilmente se supportano un'interfaccia specifica.

Per verificare se un oggetto implementa una determinata interfaccia:

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

Per verificare se un tipo implementa una determinata interfaccia:

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

Se hai un oggetto generico e vuoi fare un cast, oltre a verificare se l'interfaccia che stai lanciando è implementata, il codice è:

 var myCastedObject = myObject as IMyInterface;

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

2

IsAssignableFromè ora spostato in TypeInfo:

typeof(ISMSRequest).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo());

1

Chiunque cerchi questo potrebbe trovare utile il seguente metodo di estensione:

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

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

0

che dire

if(MyType as IMyInterface != null)

?


4
Questo è ovvio quando ho un'istanza. Non utile quando ho un tipo da riflessione
edc65

0

Che dire

typeof(IWhatever).GetTypeInfo().IsInterface

0

Una risposta corretta è

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

Però,

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

potrebbe restituire un risultato errato, come mostrato nel codice seguente con string e 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}");
    }

risultati:

 isConvertibleDirect: True
 isConvertibleTypeAssignable: False
 isConvertibleHasInterface: True

4
Come puoi vedere nella risposta accettata, hai scambiato i tipi in uso di IsAssignableFrom. Proprio come avvertono Benjamin ed Ehouarn.
VV5198722,

0

Nota che se hai un'interfaccia generica, IMyInterface<T>questo restituirà sempre false:

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

Questo non funziona neanche:

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

Tuttavia, se MyTypeimplementa IMyInterface<MyType>questo funziona e restituisce true:

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

Tuttavia, probabilmente non conoscerai il parametro type Tin fase di esecuzione . Una soluzione un po 'confusa è:

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

La soluzione di Jeff è un po 'meno complicata:

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

Ecco un metodo di estensione Typeche funziona in ogni caso:

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

(Nota che quanto sopra usa linq, che probabilmente è più lento di un ciclo.)

È quindi possibile fare:

   typeof(MyType).IsImplementing(IMyInterface<>)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.