Como determinar se um tipo implementa uma interface com reflexão em C #


562

Será reflexão em C#oferta uma maneira de determinar se alguns dados System.Typemodelos tipo algum interface?

public interface IMyInterface {}

public class MyType : IMyInterface {}

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

Respostas:


969

Você tem poucas escolhas:

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

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

Para uma interface genérica, é um pouco diferente.

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

68
Lembre-se de que typeof (IMyInterface) .IsAssignableFrom (typeof (IMyInterface)) também é verdadeiro, o que pode ter um resultado inesperado no seu código.
22412 Chris Kemp

29
Com certeza foi fácil não prestar atenção e obter argumentos para IsAssignableFromtrás. Vou com GetInterfacesagora: p
Benjamin

12
A IsAssignableFrom(t1)variante é cerca de 3x mais rápida que a GetInterfaces().Contains(t2)contraparte no meu código.
Pierre Arnaud 15/05

24
@PierreArnaud: IsAssignableFrom eventualmente chama GetInterfaces, então provavelmente seu teste verificou primeiro o GetInterfaces e IsAssignable depois. Isso é porque GetInterfaces armazena em cache é resultados de modo que os primeiros custos de invocação mais
Panos Theof

17
Uma pequena alteração na resposta de @ Kosta. Com o C # 6, podemos fazer typeof(MyType).GetInterface(nameof(IMyInterface)) != nullum melhor tipo de segurança e refatoração.
precisa saber é


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

ou

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

34
Se você já tem uma instância da classe, uma abordagem muito melhor é simplesmente someclass is IMyInterfaceporque isso não envolve o custo da reflexão. Portanto, embora não esteja errado, não é a maneira ideal de fazê-lo.
James J. Regan IV

1
@ James - Concordo. Até o Resharper dá a mesma sugestão.
Angshuman Agarwal

@ JamesJ.ReganIV você deve postar isso como uma resposta, eu quase perdi o seu comentário
reggaeguitar

@ reggaeguitar, obrigado, mas o comentário não responde à pergunta original. A pergunta pede a solução de Reflexão, estou apenas dizendo no primeiro caso desta resposta em que você tem uma instância do objeto que a reflexão não é a solução ideal.
James J. Regan IV

1
@ JamesJ.ReganIV Na verdade, isverifica nas duas direções da hierarquia de herança, enquanto IsAssignableFromapenas verifica para cima. Além disso, se você tiver uma instância de um objeto, deverá chamar IsInstanceOfType(que também olha apenas para cima).
Sellorio 6/11

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

Eu acho que este é o lançamento correto, por três razões:

  1. Ele usa GetInterfaces e não IsAssignableFrom, é mais rápido, pois IsAssignableFrom eventualmente, depois de várias verificações, chama GetInterfaces.
  2. Ele itera sobre a matriz local, portanto não haverá verificação de limites.
  3. Ele usa o operador == definido para Type, portanto, provavelmente é mais seguro que o método Equals (que a chamada Contains, eventualmente usará).

10
1 por conteúdo, eu odeio os espaços ao redor dos parênteses e os aparelhos egípcios. Além disso, todo o método pode ser escrito como: return type.GetInterfaces (). Any (t => t == ifaceType);
reggaeguitar

1
Type.IsAssignableFrom () internaly age exatamente como o seu código
devi

1
Também porque não digitar.GetInterfaces (). Contém (ifaceType) que não usa LINQ.

9

Eu apenas fiz:

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

Eu gostaria de ter dito where I : interface, mas interfacenão é uma opção genérica de restrição de parâmetros. classé o mais próximo possível.

Uso:

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

Acabei de dizer Implementsporque é mais intuitivo. Eu sempre sou IsAssignableFromenganado.


Você pode fazer return typeof(I).IsInterface && typeof(I).IsAssignableFrom(source);para retornar false em qualquer uso 'incorreto' do método, ou seja; usando-o com um tipo de classe em vez de um tipo de interface, lance alternativamente uma exceção se o parâmetro-tipo não for uma interface. Embora você poderia argumentar que uma classe derivada 'implementos' É pai ...
Sindri Joelsson

7

Modificando a resposta de Jeff para obter o desempenho ideal (graças ao teste de desempenho de Pierre Arnaud):

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

Para encontrar todos os tipos que implementam uma interface em um dado Assembly:

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

7

Como alguém já mencionou: Benjamin 10 / abr / 13 às 22:21 "

Com certeza foi fácil não prestar atenção e obter os argumentos para IsAssignableFrom ao contrário. Vou agora com GetInterfaces: p -

Bem, outra maneira é apenas criar um método de extensão curto que atenda, até certo ponto, à maneira "mais usual" de pensar (e concordou que essa é uma escolha pessoal muito pequena para torná-la um pouco "mais natural" com base nas preferências de alguém ):

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

E por que não ficar um pouco mais genérico (bem, não tenho certeza se é realmente tão interessante, bem, eu suponho que estou passando outra pitada de açúcar 'sintaxe'):

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

Eu acho que pode ser muito mais natural assim, mas mais uma vez apenas uma questão de opiniões muito pessoais:

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

4
Existe uma razão para você não colocar a implementação diretamente no método de extensão? Quero dizer, com certeza, isso permite que você diga as duas coisas, mas por que você precisaria fazer isso?
Mark A. Donohoe

@MarqueIV triste de voltar para você quase 2 anos de atraso, bem, eu acho que foi um velho mau hábito na época para embrulhar ajudante método no método de extensão para evitar a repetição de código, vai editar a minha resposta :)
Kerry Perret

1
O @MarqueIV done plus mudou meu outro mau hábito de não usar alias, ou seja, Boolean=> bool(não sei por que costumava ter algumas regras "sofisticadas" de codificação quando era mais jovem).
Kerry Perret

3

Se você tem um tipo ou uma instância, pode verificar facilmente se eles suportam uma interface específica.

Para testar se um objeto implementa uma certa interface:

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

Para testar se um tipo implementa uma certa interface:

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

Se você obteve um objeto genérico e deseja fazer uma conversão, bem como verificar se a interface para a qual você lança está implementada, o código é:

 var myCastedObject = myObject as IMyInterface;

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

2

IsAssignableFromagora é movido para TypeInfo:

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

1

Qualquer pessoa que procure por isso pode achar útil o seguinte método de extensão:

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

testes de unidade:

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

A respeito

if(MyType as IMyInterface != null)

?


4
Isso é óbvio quando eu tenho uma instância. Não é útil quando tenho um tipo de reflexão
edc65

0

A respeito

typeof(IWhatever).GetTypeInfo().IsInterface

0

Uma resposta correta é

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

Contudo,

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

pode retornar um resultado errado, como o seguinte código mostra com 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

4
Como você pode ver na resposta aceita, você trocou os tipos de uso de IsAssignableFrom. Assim como Benjamin e Ehouarn avisam.
VV5198722

0

Observe que se você tiver uma interface genérica IMyInterface<T>, ela sempre retornará false:

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

Isso também não funciona:

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

No entanto, se MyTypeimplementar IMyInterface<MyType>isso funciona e retorna true:

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

No entanto, você provavelmente não saberá o parâmetro type Tem tempo de execução . Uma solução um pouco hacky é:

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

A solução de Jeff é um pouco menos invasiva:

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

Aqui está um método de extensão Typeque funciona para qualquer 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);
    }
}

(Observe que o acima usa linq, que é provavelmente mais lento que um loop.)

Você pode então fazer:

   typeof(MyType).IsImplementing(IMyInterface<>)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.