Cómo determinar si un tipo implementa una interfaz con reflexión C #


562

Qué reflexión en C#ofrecer una manera de determinar si algunos dados System.Typemodelos tipo de alguna de las interfaces?

public interface IMyInterface {}

public class MyType : IMyInterface {}

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

Respuestas:


969

Tienes algunas opciones:

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

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

Para una interfaz genérica, es un poco diferente.

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

68
Recuerde que typeof (IMyInterface) .IsAssignableFrom (typeof (IMyInterface)) también es cierto, lo que puede tener un resultado inesperado en su código.
Chris Kemp el

29
Seguro que fue fácil no prestar atención y obtener los argumentos al IsAssignableFromrevés. Iré GetInterfacesahora con : p
Benjamin

12
La IsAssignableFrom(t1)variante es aproximadamente 3 veces más rápida que la GetInterfaces().Contains(t2)contraparte en mi código.
Pierre Arnaud

24
@PierreArnaud: IsAssignableFrom eventualmente llama a GetInterfaces, por lo que probablemente su prueba verificó GetInterfaces primero e IsAssignable después. Esto se debe a que GetInterfaces almacena en caché sus resultados, por lo que la primera invocación cuesta más
Panos Theof

17
Un pequeño cambio en la respuesta de @ Kosta. Con C # 6 podemos typeof(MyType).GetInterface(nameof(IMyInterface)) != nullmejorar la seguridad de los tipos y la refactorización.
aholmes


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

o

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

34
Si ya tiene una instancia de la clase, un enfoque mucho mejor es simplemente someclass is IMyInterfaceque no implica el costo de la reflexión en absoluto. Entonces, aunque no está mal, no es una forma ideal de hacerlo.
James J. Regan IV

1
@James - De acuerdo. Incluso Resharper da la misma sugerencia.
Angshuman Agarwal

@ JamesJ.ReganIV deberías publicar eso como respuesta, casi me
pierdo

@reggaeguitar, gracias, pero el comentario no responde a la pregunta original. La pregunta pide la solución de Reflexión, solo digo que en el primer caso de esta respuesta donde tienes una instancia del objeto, la reflexión no es la solución ideal.
James J. Regan IV

1
@ JamesJ.ReganIV En realidad, isverifica en ambas direcciones de la jerarquía de herencia, mientras que IsAssignableFromsolo verifica hacia arriba. Además, si tiene una instancia de un objeto, debe llamar IsInstanceOfType(que también solo mira hacia arriba).
Sellorio el

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

Creo que esta es la versión correcta, por tres razones:

  1. Utiliza GetInterfaces y no IsAssignableFrom, es más rápido ya que IsAssignableFrom finalmente después de varias comprobaciones llama a GetInterfaces.
  2. Se itera sobre la matriz local, por lo que no habrá verificaciones de límites.
  3. Utiliza el operador == que está definido para Tipo, por lo que probablemente sea más seguro que el método Equals (que la llamada Contiene, eventualmente usará).

10
+1 por contenido, odio los espacios alrededor de los parens y las llaves egipcias. Además, todo el método se puede escribir como: return type.GetInterfaces (). Any (t => t == ifaceType);
reggaeguitar

1
Type.IsAssignableFrom () actúa internamente exactamente como su código
devi

1
Además, ¿por qué no teclea. GetInterfaces (). Contiene (ifaceType) que no usa LINQ.

9

Lo acabo de hacer:

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

Desearía haberlo dicho where I : interface, pero interfaceno es una opción genérica de restricción de parámetros. classestá lo más cerca posible.

Uso:

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

Solo dije Implementsporque eso es más intuitivo. Siempre me IsAssignableFromflip-flop.


Podría hacer return typeof(I).IsInterface && typeof(I).IsAssignableFrom(source);para devolver falso en cualquier uso 'incorrecto' del método, es decir; utilizándolo con un tipo de clase en lugar de un tipo de interfaz, alternativamente, arroje una excepción si el parámetro tipo no es una interfaz. Aunque podría argumentar que una clase derivada 'implementa' es su padre ...
Sindri Jóelsson

7

Modificación de la respuesta de Jeff para un rendimiento óptimo (gracias a la prueba de rendimiento de Pierre Arnaud):

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

Para buscar todos los tipos que implementan una interfaz en un determinado Assembly:

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

7

Como alguien más ya mencionó: Benjamin Abr 10 '13 a las 22:21 "

Seguro que fue fácil no prestar atención y obtener los argumentos para IsAssignableFrom al revés. Iré con GetInterfaces ahora: p -

Bueno, otra forma de evitarlo es crear un método de extensión breve que cumpla, hasta cierto punto, la forma de pensar "más habitual" (y acordó que esta es una elección personal muy pequeña para que sea un poco "más natural" en función de las preferencias personales) ):

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

¿Y por qué no ser un poco más genérico?

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

Creo que podría ser mucho más natural de esa manera, pero una vez más solo es cuestión de opiniones muy personales:

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

44
¿Hay alguna razón por la que no haya puesto la implementación directamente en el método de extensión? Quiero decir, seguro, esto te permite llamarlo en ambos sentidos, pero ¿por qué necesitarías hacer eso?
Mark A. Donohoe

@MarqueIV lamento contactarte casi 2 años tarde, bueno, supongo que era un mal hábito en aquel entonces envolver el método auxiliar en el método de extensión para evitar repetir el código, editaré mi respuesta :)
Kerry Perret

1
@MarqueIV done plus cambió mi otro mal hábito de no usar alias, es decir, Boolean=> bool(no sé por qué solía tener algunas reglas estrictas "elegantes" de codificación cuando era más joven).
Kerry Perret

3

Si tiene un tipo o una instancia, puede verificar fácilmente si son compatibles con una interfaz específica.

Para probar si un objeto implementa una determinada interfaz:

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

Para probar si un tipo implementa una determinada interfaz:

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

Si tiene un objeto genérico y desea realizar una conversión, así como verificar si la interfaz a la que está implementado, el código es:

 var myCastedObject = myObject as IMyInterface;

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

2

IsAssignableFromahora se mueve a TypeInfo:

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

1

Cualquiera que busque esto puede encontrar útil el siguiente método de extensión:

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

pruebas de 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

qué pasa

if(MyType as IMyInterface != null)

?


44
Esto es obvio cuando tengo una instancia. No es útil cuando tengo un tipo de reflexión
edc65

0

Qué pasa

typeof(IWhatever).GetTypeInfo().IsInterface

0

Una respuesta correcta es

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

Sin embargo,

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

podría devolver un resultado incorrecto, como se muestra en el siguiente código 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}");
    }

Resultados:

 isConvertibleDirect: True
 isConvertibleTypeAssignable: False
 isConvertibleHasInterface: True

44
Como puede ver en la respuesta aceptada, intercambió los tipos de uso de IsAssignableFrom. Tal como lo advierten Benjamin y Ehouarn.
VV5198722

0

Tenga en cuenta que si tiene una interfaz genérica IMyInterface<T>, esto siempre devolverá false:

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

Esto tampoco funciona:

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

Sin embargo, si MyTypeimplementa IMyInterface<MyType>esto funciona y devuelve true:

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

Sin embargo, es probable que no conozca el parámetro de tipo Ten tiempo de ejecución . Una solución algo hacky es:

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

La solución de Jeff es un poco menos hacky:

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

Aquí hay un método de extensión Typeque funciona para cualquier 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);
    }
}

(Tenga en cuenta que lo anterior usa linq, que probablemente es más lento que un bucle).

Entonces puedes hacer:

   typeof(MyType).IsImplementing(IMyInterface<>)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.