So bestimmen Sie, ob ein Typ eine Schnittstelle mit C # -Reflexion implementiert

562

Hat Reflexion in C#Angebot eine Möglichkeit , wenn einige gegeben , um zu bestimmen System.TypeTyp Modelle einige Schnittstelle?

public interface IMyInterface {}

public class MyType : IMyInterface {}

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

Antworten:

969

Sie haben einige Möglichkeiten:

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

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

Bei einer generischen Schnittstelle ist das etwas anders.

typeof(MyType).GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMyInterface<>))
Jeff
quelle
68
Denken Sie daran, dass typeof (IMyInterface) .IsAssignableFrom (typeof (IMyInterface)) ebenfalls wahr ist, was zu einem unerwarteten Ergebnis in Ihrem Code führen kann.
Chris Kemp
29
Es war sicher leicht, nicht darauf zu achten und die Argumente für IsAssignableFromrückwärts zu bekommen. Ich werde jetzt mit gehen GetInterfaces: p
Benjamin
12
Die IsAssignableFrom(t1)Variante ist ca. 3x schneller als das GetInterfaces().Contains(t2)Gegenstück in meinem Code.
Pierre Arnaud
24
@PierreArnaud: IsAssignableFrom ruft schließlich GetInterfaces auf, sodass Ihr Test wahrscheinlich zuerst die GetInterfaces und danach IsAssignable überprüft hat. Das liegt daran, dass GetInterfaces die Ergebnisse zwischenspeichert, sodass der erste Aufruf mehr kostet
Panos Theof
17
Eine kleine Änderung an @ Kostas Antwort. Mit C # 6 können wir typeof(MyType).GetInterface(nameof(IMyInterface)) != nullfür mehr Typensicherheit und Refactoring sorgen.
Aholmes
74

Verwendung Type.IsAssignableFrom:

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

oder

typeof(IMyInterface).IsAssignableFrom(typeof(MyType));
Ajma
quelle
34
Wenn Sie bereits eine Instanz der Klasse haben, ist ein viel besserer Ansatz einfach, someclass is IMyInterfaceda dies überhaupt keine Kosten für die Reflexion mit sich bringt. Es ist zwar nicht falsch, aber kein idealer Weg, dies zu tun.
James J. Regan IV
1
@ James - stimme zu. Sogar Resharper gibt den gleichen Vorschlag.
Angshuman Agarwal
@ JamesJ.ReganIV Sie sollten das als Antwort posten, ich habe Ihren Kommentar fast verpasst
reggaeguitar
@reggaeguitar, danke, aber der Kommentar beantwortet nicht die ursprüngliche Frage. Die Frage fragt nach der Reflexionslösung. Ich sage nur im ersten Fall dieser Antwort, dass Sie eine Instanz der Objektreflexion haben, die nicht die ideale Lösung ist.
James J. Regan IV
1
@ JamesJ.ReganIV isÜberprüft tatsächlich in beide Richtungen der Vererbungshierarchie, während IsAssignableFromnur nach oben geprüft wird. Wenn Sie eine Instanz eines Objekts haben, sollten Sie auch aufrufen IsInstanceOfType(das auch nur nach oben schaut).
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;
}

Ich denke, dies ist aus drei Gründen die richtige Version:

  1. Es verwendet GetInterfaces und nicht IsAssignableFrom, es ist schneller, da IsAssignableFrom schließlich nach mehreren Überprüfungen GetInterfaces aufruft.
  2. Es iteriert über das lokale Array, sodass keine Begrenzungsprüfungen durchgeführt werden.
  3. Es verwendet den Operator ==, der für Type definiert ist, und ist daher wahrscheinlich sicherer als die Equals-Methode (die der Contains-Aufruf möglicherweise verwendet).
Panos Theof
quelle
10
+1 für den Inhalt, ich hasse die Leerzeichen um die Parens und die ägyptischen Klammern. Die gesamte Methode kann auch wie folgt geschrieben werden: return type.GetInterfaces (). Any (t => t == ifaceType);
Reggaeguitar
1
Type.IsAssignableFrom () verhält sich intern genau wie Ihr Code
devi
1
Warum auch nicht type.GetInterfaces (). Enthält (ifaceType), das LINQ nicht verwendet.
9

Ich habe gerade getan:

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

Ich wünschte, ich hätte sagen können where I : interface, ist aber interfacekeine generische Option für Parametereinschränkungen. classist so nah wie es nur geht.

Verwendungszweck:

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

Ich habe nur gesagt, Implementsweil das intuitiver ist. Ich bekomme immer IsAssignableFromFlip-Flops.

toddmo
quelle
Sie können tun return typeof(I).IsInterface && typeof(I).IsAssignableFrom(source);, um bei "falschen" Verwendungen der Methode false zurückzugeben. Wenn Sie es mit einem Klassentyp anstelle eines Schnittstellentyps verwenden, können Sie alternativ eine Ausnahme auslösen, wenn der Typparameter keine Schnittstelle ist. Obwohl Sie argumentieren könnten, dass eine abgeleitete Klasse ihre Eltern "implementiert" ...
Sindri Jóelsson
7

Ändern von Jeffs Antwort für optimale Leistung (dank Leistungstest von Pierre Arnaud):

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

So finden Sie alle Typen, die eine Schnittstelle in einem bestimmten Bereich implementieren Assembly:

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

Wie schon jemand anderes erwähnt: Benjamin 10. April 13 um 22:21 "

Es war sicher einfach, nicht darauf zu achten und die Argumente für IsAssignableFrom rückwärts zu bekommen. Ich werde jetzt mit GetInterfaces gehen: p -

Ein anderer Weg besteht darin, eine kurze Erweiterungsmethode zu erstellen, die zum Teil die "üblichste" Denkweise erfüllt (und sich einig ist, dass dies eine sehr kleine persönliche Entscheidung ist, um sie je nach den eigenen Vorlieben etwas "natürlicher" zu machen ):

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

Und warum nicht ein bisschen allgemeiner vorgehen (na ja, ich bin mir nicht sicher, ob es wirklich so interessant ist, ich nehme an, ich gebe nur eine weitere Prise 'Syntaxing'-Zucker):

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

Ich denke, dass es auf diese Weise viel natürlicher sein könnte, aber noch einmal nur eine Frage sehr persönlicher Meinungen:

var isTrue = michelleType.IsAssignableTo<IMaBelle>();
Kerry Perret
quelle
4
Gibt es einen Grund, warum Sie die Implementierung nicht einfach direkt in die Erweiterungsmethode eingefügt haben? Ich meine, damit können Sie es in beide Richtungen nennen, aber warum sollten Sie das jemals tun müssen?
Mark A. Donohoe
@MarqueIV Entschuldigung, ich melde mich fast 2 Jahre zu spät bei Ihnen. Nun, ich denke, es war damals eine alte schlechte Angewohnheit, die Hilfsmethode in die Erweiterungsmethode einzuschließen, um das Wiederholen von Code zu vermeiden. Ich werde meine Antwort bearbeiten :)
Kerry Perret
1
@MarqueIV done plus hat meine andere schlechte Angewohnheit geändert, keinen Alias ​​zu verwenden, dh Boolean=> bool(Ich weiß nicht, warum ich in meiner Jugend einige strenge "ausgefallene" Codierungsregeln hatte).
Kerry Perret
3

Wenn Sie einen Typ oder eine Instanz haben, können Sie leicht überprüfen, ob diese eine bestimmte Schnittstelle unterstützen.

So testen Sie, ob ein Objekt eine bestimmte Schnittstelle implementiert:

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

So testen Sie, ob ein Typ eine bestimmte Schnittstelle implementiert:

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

Wenn Sie ein generisches Objekt haben und eine Umwandlung sowie eine Überprüfung durchführen möchten, ob die Schnittstelle, in die Sie umgewandelt haben, implementiert ist, lautet der Code:

 var myCastedObject = myObject as IMyInterface;

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

quelle
2

IsAssignableFromwird jetzt verschoben nach TypeInfo:

typeof(ISMSRequest).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo());
Codeputer
quelle
1

Jeder, der danach sucht, könnte die folgende Erweiterungsmethode nützlich finden:

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

Xunit-Tests:

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
quelle
0

wie wäre es mit

if(MyType as IMyInterface != null)

?

Pingi
quelle
4
Dies ist offensichtlich, wenn ich eine Instanz habe. Nicht nützlich, wenn ich einen Typ aus Reflexion habe
edc65
0

Wie wäre es mit

typeof(IWhatever).GetTypeInfo().IsInterface
LaWi
quelle
0

Eine richtige Antwort ist

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

Jedoch,

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

Möglicherweise wird ein falsches Ergebnis zurückgegeben, wie der folgende Code mit string und IConvertible zeigt:

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

Ergebnisse:

 isConvertibleDirect: True
 isConvertibleTypeAssignable: False
 isConvertibleHasInterface: True
EricBDev
quelle
4
Wie Sie in der akzeptierten Antwort sehen können, haben Sie die verwendeten Typen von ausgetauscht IsAssignableFrom. Genau wie Benjamin und Ehouarn warnen.
VV5198722
0

Beachten Sie, dass bei einer generischen Schnittstelle IMyInterface<T>immer Folgendes zurückgegeben wird false:

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

Das funktioniert auch nicht:

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

Wenn dies jedoch MyTypeimplementiert IMyInterface<MyType>wird, funktioniert dies und gibt Folgendes zurück true:

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

Wahrscheinlich kennen Sie den Typparameter Tzur Laufzeit jedoch nicht . Eine etwas hackige Lösung ist:

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

Jeffs Lösung ist etwas weniger hackig:

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

Hier ist eine Erweiterungsmethode Type, die in jedem Fall funktioniert:

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

(Beachten Sie, dass oben linq verwendet wird, das wahrscheinlich langsamer als eine Schleife ist.)

Sie können dann tun:

   typeof(MyType).IsImplementing(IMyInterface<>)
Diego
quelle