Obtendo todos os tipos que implementam uma interface

553

Usando a reflexão, como posso obter todos os tipos que implementam uma interface com C # 3.0 / .NET 3.5 com o mínimo de código e minimizando iterações?

É isso que quero reescrever:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff
juan
fonte
1
O código de exemplo funciona? Tenho falsos negativos com a sua condição if.
Imperador Orionii 15/12
3
A instrução if no código acima sempre será falsa, porque você está testando se uma instância da classe Type (t) implementa sua interface, o que não será, a menos que o Type herda IMyInterface (nesse caso, sempre será verdadeiro).
Liazy 26/06

Respostas:

807

O meu seria isso em c # 3.0 :)

var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => type.IsAssignableFrom(p));

Basicamente, a menor quantidade de iterações sempre será:

loop assemblies  
 loop types  
  see if implemented.
Darren Kopp
fonte
194
Observe que a lista também pode incluir a própria interface. Altere a última linha para .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);para filtrá-la (ou p.IsClass).
Jtpereyda
39
Nota: Esta resposta está errada !, verifica "Compatibilidade de atribuição" e não se a interface está implementada não. Por exemplo, List<string>não é implementado, IEnumerable<object>mas esse método retornará verdadeiro no .Net 4.0 devido à covariância que está realmente errada. A resposta correta está aqui #
Sriram Sakthivel
20
@SriramSakthivel primeiro, valores genéricos não foram especificados. Segundo, essa pergunta antecede a covariância. Terceiro, você assume que o retorno covariante não é algo que eles desejam.
Darren Kopp
24
Você está absolutamente certo, querida, eu sei que esse é um tópico antigo, acabei de registrar meu comentário apenas para que os futuros usuários tomem consciência de que esse problema existe. Não te ofender. e, como o título da pergunta diz, se o OP está pedindo Como obter todos os tipos que implementam uma interface, esse código não está fazendo isso. mas quase todos os casos funciona , sem dúvida. também existem casos extremos, como eu disse. Apenas para estar ciente disso;
precisa saber é o seguinte
9
Também precisará garantir que a classe não seja abstrata =>.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
Jonesopolis 23/12/2015
66

Isso funcionou para mim. Ele percorre as classes e verifica se elas são derivadas de myInterface

 foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                 .Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
    //do stuff
 }
Ben Watkins
fonte
5
Você está assumindo que o assembly está no executável principal. Não é um projeto adicional. Você também está iterando desnecessariamente através de várias iterações. É melhor que a estrutura faça o trabalho pesado. Em seguida, filtre mais para baixo quando encontrado. Se relevante, atualize sua resposta. Inclua o raciocínio da lista <T>. var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies (). SelectMany (x => x.GetTypes ()). Where (mytype => typeof (myInterface) .IsAssignableFrom (mytype) && mytype.GetInterfaces (). )); foreach (item var nos itens) Console.Log (item.Name);
TamusJRoyce 29/08
58

Para encontrar todos os tipos em um assembly que implementa a interface IFoo:

var results = from type in someAssembly.GetTypes()
              where typeof(IFoo).IsAssignableFrom(type)
              select type;

Observe que a sugestão de Ryan Rinaldi estava incorreta. Ele retornará 0 tipos. Você não pode escrever

where type is IFoo

porque type é uma instância System.Type e nunca será do tipo IFoo. Em vez disso, você verifica se o IFoo é atribuível a partir do tipo. Isso obterá os resultados esperados.

Além disso, a sugestão de Adam Wright, que atualmente está marcada como resposta, também está incorreta e pelo mesmo motivo. No tempo de execução, você verá 0 tipos retornados, porque todas as instâncias System.Type não eram implementadores do IFoo.

Judah Gabriel Himango
fonte
58

Compreendo que essa é uma pergunta muito antiga, mas pensei em adicionar outra resposta para futuros usuários, pois todas as respostas até o momento usam alguma forma de Assembly.GetTypes.

Embora GetTypes () realmente retorne todos os tipos, isso não significa necessariamente que você possa ativá-los e, portanto, potencialmente lançar a ReflectionTypeLoadException.

Um exemplo clássico de não poder ativar um tipo seria quando o tipo retornado é derivedde, basemas baseé definido em um assembly diferente daquele de derived, um assembly ao qual o assembly de chamada não faz referência.

Então diga que temos:

Class A // in AssemblyA
Class B : Class A, IMyInterface // in AssemblyB
Class C // in AssemblyC which references AssemblyB but not AssemblyA

Se em ClassCque está AssemblyC, faremos algo conforme a resposta aceita:

var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => type.IsAssignableFrom(p));

Então ele jogará a ReflectionTypeLoadException.

Isso ocorre porque sem uma referência a AssemblyA em AssemblyCque você não seria capaz de:

var bType = typeof(ClassB);
var bClass = (ClassB)Activator.CreateInstance(bType);

Em outras palavras, ClassBnão é carregável, o que é algo que a chamada para GetTypes verifica e ativa.

Portanto, para qualificar com segurança o conjunto de resultados para tipos carregáveis, de acordo com este artigo de Phil Haacked Obter todos os tipos em um código Assembly e Jon Skeet, você faria algo como:

public static class TypeLoaderExtensions {
    public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) {
        if (assembly == null) throw new ArgumentNullException("assembly");
        try {
            return assembly.GetTypes();
        } catch (ReflectionTypeLoadException e) {
            return e.Types.Where(t => t != null);
        }
    }
}

E depois:

private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
    var it = typeof (IMyInterface);
    return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}
arriscar
fonte
3
Isso me ajudou a lidar com um problema super estranho, onde, no meu projeto de teste, os GetTypes falhariam e apenas em nosso ambiente de IC. GetLoadableTypes foi uma correção para esta solução. O erro não seria reproduzível no ambiente local e era o seguinte: System.Reflection.ReflectionTypeLoadException: Não foi possível carregar um ou mais dos tipos solicitados. Recupere a propriedade LoaderExceptions para obter mais informações. Mais especificamente, estava reclamando que havia um tipo que não tinha uma implementação concreta e isso aconteceu no projeto de teste de unidade. Obrigado por isso!
Lari Tuomisto
2
Essa resposta deve ser marcado como solução, ele salvou minha bunda hoje, porque como @Lari Tuomisto disse, em env locais não poderíamos re-produto de erro semelhante
LIGHTNING3
3
Caso isso ajude outra pessoa: esta solução funcionou para mim, mas tive que modificá-la para remover o tipo de interface da lista. Eu queria ativar CreateInstancepara todos eles, e uma exceção foi lançada ao tentar criar a interface real (o que me deixou confuso por um tempo quando pensei que a interface real estava fora do caminho nesta solução). Então mudei o código para GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).ToList();.
Xavier Peña
21

Outras respostas aqui são usadas IsAssignableFrom. Você também pode usar FindInterfacesno Systemespaço para nome, conforme descrito aqui .

Aqui está um exemplo que verifica todos os assemblies na pasta do assembly em execução no momento, procurando por classes que implementam uma certa interface (evitando o LINQ para maior clareza).

static void Main() {
    const string qualifiedInterfaceName = "Interfaces.IMyInterface";
    var interfaceFilter = new TypeFilter(InterfaceFilter);
    var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    var di = new DirectoryInfo(path);
    foreach (var file in di.GetFiles("*.dll")) {
        try {
            var nextAssembly = Assembly.ReflectionOnlyLoadFrom(file.FullName);
            foreach (var type in nextAssembly.GetTypes()) {
                var myInterfaces = type.FindInterfaces(interfaceFilter, qualifiedInterfaceName);
                if (myInterfaces.Length > 0) {
                    // This class implements the interface
                }
            }
        } catch (BadImageFormatException) {
            // Not a .net assembly  - ignore
        }
    }
}

public static bool InterfaceFilter(Type typeObj, Object criteriaObj) {
    return typeObj.ToString() == criteriaObj.ToString();
}

Você pode configurar uma lista de interfaces se desejar corresponder a mais de uma.

hillstuk
fonte
Este procura o nome da interface da string, o que eu estava procurando.
senthil
Funciona ao carregar uma montagem em um domínio diferente, pois o tipo precisa ser serializado em uma sequência. muito fantástico!
TamusJRoyce 20/05
Eu recebo: Não é possível resolver a dependência do assembly 'System.Core, Versão = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089' porque ele não foi pré-carregado. Ao usar as APIs ReflectionOnly, os assemblies dependentes devem ser pré-carregados ou carregados sob demanda por meio do evento ReflectionOnlyAssemblyResolve.
Bkwdesign 03/10/19
18

faça um loop em todos os assemblies carregados, faça um loop em todos os seus tipos e verifique se eles implementam a interface.

algo como:

Type ti = typeof(IYourInterface);
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) {
    foreach (Type t in asm.GetTypes()) {
        if (ti.IsAssignableFrom(t)) {
            // here's your type in t
        }
    }
}
Lasse V. Karlsen
fonte
8

Isso funcionou para mim (se você deseja excluir tipos de sistema na pesquisa):

Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
        t => lookupType.IsAssignableFrom(t) && !t.IsInterface); 
Carl Nayak
fonte
5

Editar: Acabei de ver a edição para esclarecer que a pergunta original era para a redução de iterações / código e isso é muito bom como exercício, mas em situações do mundo real, você deseja a implementação mais rápida, independentemente de quão legal é o LINQ subjacente.

Aqui está o meu método Utils para percorrer os tipos carregados. Ele lida com classes regulares e interfaces, e a opção excludeSystemTypes acelera enormemente as coisas se você estiver procurando implementações em sua base de código própria / de terceiros.

public static List<Type> GetSubclassesOf(this Type type, bool excludeSystemTypes) {
    List<Type> list = new List<Type>();
    IEnumerator enumerator = Thread.GetDomain().GetAssemblies().GetEnumerator();
    while (enumerator.MoveNext()) {
        try {
            Type[] types = ((Assembly) enumerator.Current).GetTypes();
            if (!excludeSystemTypes || (excludeSystemTypes && !((Assembly) enumerator.Current).FullName.StartsWith("System."))) {
                IEnumerator enumerator2 = types.GetEnumerator();
                while (enumerator2.MoveNext()) {
                    Type current = (Type) enumerator2.Current;
                    if (type.IsInterface) {
                        if (current.GetInterface(type.FullName) != null) {
                            list.Add(current);
                        }
                    } else if (current.IsSubclassOf(type)) {
                        list.Add(current);
                    }
                }
            }
        } catch {
        }
    }
    return list;
}

Não é bonito, eu admito.

tags2k
fonte
2
Os enumeradores implementam IDisposable que não está sendo descartado em uma tentativa / finalmente. É melhor usar um foreach ou linq.
TamusJRoyce
Por que você está testando excludeSystemTypesduas vezes em um if?
NetMage 11/11/19
4

Outra resposta não estava funcionando com uma interface genérica .

Nesse caso, basta substituir typeof (ISomeInterface) por typeof (T).

List<string> types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
            .Where(x => typeof(ISomeInterface).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
            .Select(x => x.Name).ToList();

Então com

AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())

nós temos todas as assembléias

!x.IsInterface && !x.IsAbstract

é usado para excluir a interface e as abstratas e

.Select(x => x.Name).ToList();

tê-los em uma lista.

Antonin GAVREL
fonte
Explique como sua solução funciona e por que ela é superior a todas as outras respostas.
Lukas Körfer 19/09/18
Não é superior ou inferior, as outras respostas não funcionaram para mim e eu me preocupei em compartilhá-lo.
Antonin GAVREL
Meu comentário foi apenas sobre sua resposta ser apenas código, então pedi para você adicionar algumas explicações.
Lukas Körfer
2

Não há uma maneira fácil (em termos de desempenho) de fazer o que você deseja fazer.

O Reflection trabalha principalmente com montagens e tipos, portanto você terá que obter todos os tipos de montagem e consultá-los para obter a interface correta. Aqui está um exemplo:

Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);

Isso fornecerá a você todos os tipos que implementam o IMyInterface no Assembly MyAssembly

Jorge Córdoba
fonte
2

Ainda melhor ao escolher o local da montagem. Filtre a maioria dos assemblies se você souber que todas as suas interfaces implementadas estão dentro do mesmo Assembly.DefinedTypes.

// We get the assembly through the base class
var baseAssembly = typeof(baseClass).GetTypeInfo().Assembly;

// we filter the defined classes according to the interfaces they implement
var typeList = baseAssembly.DefinedTypes.Where(type => type.ImplementedInterfaces.Any(inter => inter == typeof(IMyInterface))).ToList();

Por Can Bilgin

user489566
fonte
1

Já existem muitas respostas válidas, mas eu gostaria de adicionar outra implementação como uma extensão de tipo e uma lista de testes de unidade para demonstrar diferentes cenários:

public static class TypeExtensions
{
    public static IEnumerable<Type> GetAllTypes(this Type type)
    {
        var typeInfo = type.GetTypeInfo();
        var allTypes = GetAllImplementedTypes(type).Concat(typeInfo.ImplementedInterfaces);
        return allTypes;
    }

    private static IEnumerable<Type> GetAllImplementedTypes(Type type)
    {
        yield return type;
        var typeInfo = type.GetTypeInfo();
        var baseType = typeInfo.BaseType;
        if (baseType != null)
        {
            foreach (var foundType in GetAllImplementedTypes(baseType))
            {
                yield return foundType;
            }
        }
    }
}

Este algoritmo suporta os seguintes cenários:

public static class GetAllTypesTests
{
    public class Given_A_Sample_Standalone_Class_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(SampleStandalone);

            _expectedTypes =
                new List<Type>
                {
                    typeof(SampleStandalone),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Abstract_Base_Class_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(SampleBase);

            _expectedTypes =
                new List<Type>
                {
                    typeof(SampleBase),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Child_Class_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(SampleChild);

            _expectedTypes =
                new List<Type>
                {
                    typeof(SampleChild),
                    typeof(SampleBase),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Base_Interface_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(ISampleBase);

            _expectedTypes =
                new List<Type>
                {
                    typeof(ISampleBase)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Child_Interface_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(ISampleChild);

            _expectedTypes =
                new List<Type>
                {
                    typeof(ISampleBase),
                    typeof(ISampleChild)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Implementation_Class_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        protected override void Given()
        {
            _sut = typeof(SampleImplementation);

            _expectedTypes =
                new List<Type>
                {
                    typeof(SampleImplementation),
                    typeof(SampleChild),
                    typeof(SampleBase),
                    typeof(ISampleChild),
                    typeof(ISampleBase),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    public class Given_A_Sample_Interface_Instance_Type_When_Getting_All_Types
        : Given_When_Then_Test
    {
        private Type _sut;
        private IEnumerable<Type> _expectedTypes;
        private IEnumerable<Type> _result;

        class Foo : ISampleChild { }

        protected override void Given()
        {
            var foo = new Foo();
            _sut = foo.GetType();

            _expectedTypes =
                new List<Type>
                {
                    typeof(Foo),
                    typeof(ISampleChild),
                    typeof(ISampleBase),
                    typeof(object)
                };
        }

        protected override void When()
        {
            _result = _sut.GetAllTypes();
        }

        [Fact]
        public void Then_It_Should_Return_The_Right_Type()
        {
            _result.Should().BeEquivalentTo(_expectedTypes);
        }
    }

    sealed class SampleStandalone { }
    abstract class SampleBase { }
    class SampleChild : SampleBase { }
    interface ISampleBase { }
    interface ISampleChild : ISampleBase { }
    class SampleImplementation : SampleChild, ISampleChild { }
}
diegosasw
fonte
0
   public IList<T> GetClassByType<T>()
   {
        return AppDomain.CurrentDomain.GetAssemblies()
                          .SelectMany(s => s.GetTypes())
                          .ToList(p => typeof(T)
                          .IsAssignableFrom(p) && !p.IsAbstract && !p.IsInterface)
                          .SelectList(c => (T)Activator.CreateInstance(c));
   }
Jonathan Santiago
fonte
0

Eu tenho exceções no código linq, então faço dessa maneira (sem uma extensão complicada):

private static IList<Type> loadAllImplementingTypes(Type[] interfaces)
{
    IList<Type> implementingTypes = new List<Type>();

    // find all types
    foreach (var interfaceType in interfaces)
        foreach (var currentAsm in AppDomain.CurrentDomain.GetAssemblies())
            try
            {
                foreach (var currentType in currentAsm.GetTypes())
                    if (interfaceType.IsAssignableFrom(currentType) && currentType.IsClass && !currentType.IsAbstract)
                        implementingTypes.Add(currentType);
            }
            catch { }

    return implementingTypes;
}
akop
fonte
-3

Você pode usar algum LINQ para obter a lista:

var types = from type in this.GetType().Assembly.GetTypes()
            where type is ISomeInterface
            select type;

Mas sério, isso é mais legível?

Ryan Rinaldi
fonte
6
Pode ser mais legível, se funcionou. Infelizmente, sua cláusula where está verificando se uma instância da classe System.Type implementa ISomeInterface, que nunca será verdadeira, a menos que ISomeInterface seja realmente IReflect ou ICustomAttributeProvider, nesse caso, sempre será verdadeira.
Joel Mueller
A resposta de Carl Nayak acima tem a resposta para corrigir a cláusula where: IsAssignableFrom. Erro fácil para uma resposta.
TamusJRoyce 20/05