Obtener todos los tipos que implementan una interfaz

554

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

Esto es lo que quiero volver a escribir:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff
juan
fuente
1
¿Funciona el código de ejemplo? Tengo falsos negativos con su condición if.
Emperador Orionii
3
La declaración if en el código anterior siempre será falsa porque está probando si una instancia de la clase Type (t) implementa su interfaz, lo que no hará a menos que Type herede IMyInterface (en cuyo caso siempre será verdadera).
Liazy

Respuestas:

808

El mío sería esto 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.
Darren Kopp
fuente
194
Tenga en cuenta que la lista también puede incluir la interfaz en sí. Cambie la última línea a .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);para filtrarla (o p.IsClass).
jtpereyda
39
Nota: ¡Esta respuesta es incorrecta !, esto comprueba "Compatibilidad de asignación", no si la interfaz está implementada o no. Por ejemplo, List<string>no se implementa, IEnumerable<object>pero este método devolverá verdadero en .Net 4.0 debido a la covarianza, que de hecho es incorrecta. La respuesta correcta está aquí
Sriram Sakthivel
20
@SriramSakthivel, los valores genéricos no se especificaron. En segundo lugar, esta pregunta es anterior a la covarianza. Tercero, supones que el retorno covariante no es algo que desean.
Darren Kopp
24
Tienes toda la razón, querida, sé que este es un hilo viejo, acabo de registrar mi comentario solo para que los futuros usuarios se den cuenta de que existe ese problema. No ofenderte. y como dice el título de la pregunta, si OP solicita Obtener todos los tipos que implementan una interfaz, este código no lo está haciendo. pero casi todos los casos funciona , sin duda. Hay casos de esquina también como dije. Solo para ser consciente de ello;
Sriram Sakthivel
99
También tendrá que asegurarse de que la clase no es abstracta =>.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
Jonesopolis
66

Esto funcionó para mí. Recorre las clases en bucle y comprueba si están derivadas de myInterface

 foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                 .Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
    //do stuff
 }
Ben Watkins
fuente
55
Está asumiendo que el ensamblado está en el ejecutable principal. No es un proyecto adicional. También está iterando innecesariamente a través de un montón de iteraciones. Es mejor que el marco haga el trabajo pesado. Luego, filtre más abajo cuando lo encuentre. Si es relevante, actualice su respuesta. Incluir el razonamiento List <T>. var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies (). SelectMany (x => x.GetTypes ()). Where (mytype => typeof (myInterface) .IsAssignableFrom (mytype) && mytype.GetInterfaces (). Contiene (type) )); foreach (elemento var en elementos) Console.Log (elemento.Nombre);
TamusJRoyce
58

Para buscar todos los tipos en un ensamblaje que implementan 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 era incorrecta. Devolverá 0 tipos. No puedes escribir

where type is IFoo

porque type es una instancia de System.Type y nunca será de tipo IFoo. En cambio, 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 la 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.

Judá Gabriel Himango
fuente
58

Aprecio que esta es una pregunta muy antigua, pero pensé que agregaría otra respuesta para futuros usuarios, ya que todas las respuestas hasta la fecha usan alguna forma de Assembly.GetTypes.

Si bien GetTypes () realmente devolverá todos los tipos, no necesariamente significa que podría activarlos y, por lo tanto, podría arrojar a ReflectionTypeLoadException.

Un ejemplo clásico para no poder activar un tipo sería cuando el tipo devuelto es derivedde basepero basese define en un ensamblaje diferente del de derived, un ensamblaje 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 ClassCcuál está adentro AssemblyC, entonces 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 arrojará un ReflectionTypeLoadException.

Esto se debe a que sin una referencia a AssemblyA en AssemblyCque no sería capaz de:

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

En otras palabras, ClassBno se puede cargar, que es algo que la llamada a GetTypes comprueba y lanza.

Entonces, para calificar de forma segura el conjunto de resultados para los tipos cargables, según este artículo de Phil Haacked Obtener todos los tipos en un ensamblaje y el código Jon Skeet, en su lugar, haría 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 entonces:

private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
    var it = typeof (IMyInterface);
    return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}
rismo
fuente
3
Esto me ayudó a lidiar con un problema súper extraño, donde en mi proyecto de prueba GetTypes fallaba y solo en nuestro entorno de CI. GetLoadableTypes fue una solución para esta solución. El error no sería reproducible en el entorno local y fue este: System.Reflection.ReflectionTypeLoadException: no se puede cargar uno o más de los tipos solicitados. Recupere la propiedad LoaderExceptions para más información. Más específicamente, se quejaba de que había un tipo que no tenía una implementación concreta y sucedió en el proyecto de prueba de la unidad. ¡Gracias por esto!
Lari Tuomisto
2
Esta respuesta debe marcarse como solución, me salvó el culo hoy, porque como dijo @Lari Tuomisto, en el entorno local no pudimos volver a producir un error similar
Lightning3
3
En caso de que ayude a alguien más: esta solución funcionó para mí, pero tuve que modificarla para eliminar el tipo de interfaz de la lista. Quería activar CreateInstancepara todos ellos, y se produjo una excepción cuando intentaba crear la interfaz real (lo que me confundió por un tiempo cuando pensé que la interfaz real estaba fuera del camino en esta solución). Entonces cambié el código a GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).ToList();.
Xavier Peña
21

Otras respuestas aquí usan IsAssignableFrom. También puede usar FindInterfacesdesde el Systemespacio de nombres, como se describe aquí .

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

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.

hillstuk
fuente
Este busca el nombre de la interfaz de cadena que es lo que estaba buscando.
senthil
Funciona al cargar un ensamblaje en un dominio diferente, ya que el tipo debe ser serializado en una cadena. ¡Muy impresionante!
TamusJRoyce
Obtengo: No se puede resolver la dependencia del ensamblado 'System.Core, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089' porque no se ha precargado. Cuando se utilizan las API ReflectionOnly, los ensamblajes dependientes se deben precargar o cargar a pedido mediante el evento ReflectionOnlyAssemblyResolve.
bkwdesign
18

recorrer todos los ensamblajes cargados, recorrer todos sus tipos y verificar 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
        }
    }
}
Lasse V. Karlsen
fuente
8

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

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

Edición: 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 lo genial que se ve el LINQ subyacente.

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 excludeSystemTypes acelera enormemente si está buscando implementaciones en su propia base de código / 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.

etiquetas2k
fuente
2
Los enumeradores implementan IDisposable que no se elimina en un intento / finalmente. Es mejor usar un foreach o linq.
TamusJRoyce
¿Por qué estás probando excludeSystemTypesdos veces en una if?
NetMage
4

Otra respuesta no funcionaba con una interfaz genérica .

Este sí, simplemente reemplaza 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())

obtenemos 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.

Antonin GAVREL
fuente
Explique cómo funciona su solución y por qué es superior a todas las demás respuestas.
Lukas Körfer
No es superior ni inferior, las otras respuestas no funcionaron para mí y me molesté en compartirlo.
Antonin GAVREL
Mi comentario fue sobre su respuesta siendo solo de código, por lo que le pedí que agregue alguna explicación.
Lukas Körfer
2

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

Reflection funciona con ensamblajes y tipos principalmente, por lo que tendrá que obtener todos los tipos del ensamblaje y consultarlos para obtener la interfaz correcta. Aquí hay 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 ensamblado MyAssembly

Jorge Córdoba
fuente
2

Aún mejor al elegir la ubicación de montaje. Filtre la mayoría de los ensamblajes si sabe que todas sus interfaces implementadas están dentro del mismo ensamblado.

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

usuario489566
fuente
1

Ya hay muchas respuestas válidas, pero me gustaría agregar otra implementación como una extensión de Tipo y una lista de pruebas unitarias para demostrar diferentes escenarios:

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 admite los siguientes escenarios:

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
fuente
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
fuente
0

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

Puede usar LINQ para obtener la lista:

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

Pero realmente, ¿eso es más legible?

Ryan Rinaldi
fuente
66
Podría ser más legible, si funcionara. Desafortunadamente, su cláusula where está verificando si una instancia de la clase System.Type implementa ISomeInterface, lo que nunca será cierto, a menos que ISomeInterface sea realmente IReflect o ICustomAttributeProvider, en cuyo caso siempre será cierto.
Joel Mueller
La respuesta anterior de Carl Nayak tiene la respuesta para corregir la cláusula where: IsAssignableFrom. Fácil error por una respuesta.
TamusJRoyce