Pregunta

Usando la reflexión, ¿cómo puedo obtener todos los tipos que implementan una interfaz con C# 3.0/.NET 3.5 con la menor cantidad de código y minimizando las iteraciones?

Esto es lo que quiero reescribir:

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

Solución

El mío sería este en c# 3.0 :)

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

Básicamente, la menor cantidad de iteraciones siempre será:

loop assemblies  
 loop types  
  see if implemented.

Otros consejos

Esto funcionó para mí.Recorre las clases y comprueba si se derivan de myInterface

 foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                 .Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
    //do stuff
 }

Para buscar todos los tipos en un ensamblado que implemente la interfaz IFoo:

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

Tenga en cuenta que la sugerencia de Ryan Rinaldi fue incorrecta.Devolverá 0 tipos.No puedes escribir

where type is IFoo

porque el tipo es una instancia de System.Type y nunca será del tipo IFoo.En su lugar, verifica si IFoo es asignable desde el tipo.Eso obtendrá los resultados esperados.

Además, la sugerencia de Adam Wright, que actualmente está marcada como respuesta, también es incorrecta y por la misma razón.En tiempo de ejecución, verá que regresan 0 tipos, porque todas las instancias de System.Type no fueron implementadores de IFoo.

Aprecio que esta sea una pregunta muy antigua, pero pensé en agregar otra respuesta para futuros usuarios, ya que todas las respuestas hasta la fecha utilizan algún tipo de Assembly.GetTypes.

Si bien GetTypes() de hecho devolverá todos los tipos, no significa necesariamente que pueda activarlos y, por lo tanto, podría generar un ReflectionTypeLoadException.

Un ejemplo clásico de no poder activar un tipo sería cuando el tipo devuelto es derived de base pero base se define en un conjunto diferente al de derived, un ensamblado al que el ensamblado que llama no hace referencia.

Entonces digamos que tenemos:

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

si en ClassC Qué esta en AssemblyC Luego hacemos algo según la respuesta aceptada:

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

Entonces lanzará un ReflectionTypeLoadException.

Esto se debe a que sin una referencia a AssemblyA en AssemblyC no podrías:

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

En otras palabras ClassB no es cargable que es algo que la llamada a GetTypes verifica y arroja.

Entonces, para calificar de manera segura el conjunto de resultados para tipos cargables, entonces según esto Phil Haaked artículo Obtenga todos los tipos en una asamblea y Código de Jon Skeet en su lugar harías 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);
        }
    }
}

Y luego:

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

Otras respuestas aquí usan IsAssignableFrom.También puedes usar FindInterfaces desde el System espacio de nombres, como se describe aquí.

Aquí hay un ejemplo que verifica todos los ensamblados en la carpeta del ensamblado que se está ejecutando actualmente, buscando clases que implementen una determinada interfaz (evitando LINQ para mayor claridad).

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

Puede configurar una lista de interfaces si desea hacer coincidir más de una.

recorra todos los ensamblajes cargados, recorra todos sus tipos y verifique si implementan la interfaz.

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

Esto funcionó para mí (si lo desea, puede excluir los tipos de sistemas en la búsqueda):

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

Editar:Acabo de ver la edición para aclarar que la pregunta original era para la reducción de iteraciones/código y eso está muy bien como ejercicio, pero en situaciones del mundo real querrás la implementación más rápida, independientemente de cómo enfríe el aspecto subyacente de LINQ.

Aquí está mi método Utils para iterar a través de los tipos cargados.Maneja clases regulares así como interfaces, y la opción excluirSystemTypes acelera enormemente las cosas si está buscando implementaciones en su propia base de código o de terceros.

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

No es bonito, lo admito.

Otra respuesta no funcionaba con un interfaz genérica.

Este lo hace, simplemente reemplace 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();

Entonces con

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

conseguimos todas las asambleas

!x.IsInterface && !x.IsAbstract

se utiliza para excluir la interfaz y los abstractos y

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

tenerlos en una lista.

No existe una manera fácil (en términos de rendimiento) de hacer lo que quieres hacer.

Reflection trabaja principalmente con ensamblajes y tipos, por lo que tendrá que obtener todos los tipos de ensamblaje y consultarlos para encontrar la interfaz correcta.He aquí un ejemplo:

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

Eso le proporcionará todos los tipos que implementan IMyInterface en el ensamblaje MyAssembly.

Aún mejor a la hora de elegir el lugar de montaje.Filtre la mayoría de los ensamblados si sabe que todas las interfaces implementadas están dentro del mismo 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 Bilgín

Tengo excepciones en el código linq, así que lo hago de esta manera (sin una extensión 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;
}

Podrías usar algo de LINQ para obtener la lista:

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

Pero realmente, ¿es eso más legible?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top