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

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.

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
 }

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.


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

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.


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

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

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.


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.


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


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



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

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

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

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


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.