Pergunta

Usando 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?

Isto é o que quero reescrever:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff
Foi útil?

Solução

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 será sempre:

loop assemblies  
 loop types  
  see if implemented.

Outras dicas

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
 }

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 IFoo pode ser atribuído a partir do tipo.Isso obterá os resultados esperados.

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

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

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

Um exemplo clássico de não conseguir ativar um tipo seria quando o tipo retornado é derived de base mas base é definido em uma montagem diferente daquela de derived, um assembly ao qual o assembly chamador não faz referência.

Então digamos 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 ClassC que está em AssemblyC então fazemos algo de acordo com a resposta aceita:

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

Então ele lançará um ReflectionTypeLoadException.

Isto porque sem uma referência a AssemblyA em AssemblyC você não seria capaz de:

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

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

Portanto, para qualificar com segurança o conjunto de resultados para tipos carregáveis, conforme este Phil Haacked artigo Obtenha todos os tipos em uma montagem e Código 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 então:

private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
    var it = typeof (IMyInterface);
    return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}

Outras respostas aqui usam IsAssignableFrom.Você também pode usar FindInterfaces de System espaço para nome, conforme descrito aqui.

Aqui está um exemplo que verifica todos os assemblies na pasta do assembly atualmente em execução, procurando por classes que implementem uma determinada interface (evitando 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 quiser combinar mais de uma.

percorrer todos os assemblies carregados, percorrer todos os seus tipos e verificar 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
        }
    }
}

Isso funcionou para mim (se desejar, você pode excluir os tipos de sistema na pesquisa):

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

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ê vai querer a implementação mais rápida, independentemente de como legal a aparência subjacente do LINQ.

Aqui está meu método Utils para iterar pelos tipos carregados.Ele lida com classes regulares, bem como interfaces, e a opção excludeSystemTypes acelera enormemente as coisas se você estiver procurando implementações em sua própria base de código/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, admito.

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

Este sim, 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())

temos todas as montagens

!x.IsInterface && !x.IsAbstract

é usado para excluir a interface e os abstratos e

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

para tê-los em uma lista.

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

O Reflection trabalha principalmente com montagens e tipos, então 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 lhe dará todos os tipos que implementam o IMyInterface no Assembly MyAssembly

Melhor ainda na hora de escolher o local da Montagem.Filtre a maioria dos assemblies se você souber que todas as 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

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

private static IList<Type> loadAllTypes(Types[] interfaces)
{
    IList<Type> objects = 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)
                        objects.Add(currentType);
            }
            catch { }

    return objects;
}

Você poderia usar algum LINQ para obter a lista:

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

Mas realmente, isso é mais legível?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top