c# - puede - Obteniendo todos los tipos que implementan una interfaz.
no se puede crear una instancia de la clase o interfaz abstracta c# (12)
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 utilizan algún tipo de Assembly.GetTypes
.
Si bien GetTypes () devolverá todos los tipos, no significa necesariamente que pueda activarlos y, por lo tanto, podría lanzar una ReflectionTypeLoadException
.
Un ejemplo clásico para no poder activar un tipo sería cuando el tipo devuelto se derived
de la base
pero la base
se define en un ensamblaje diferente al de derived
, un ensamblaje al que el ensamblado que llama no hace referencia.
Así que 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
que está en AssemblyC
, 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));
Luego lanzará una ReflectionTypeLoadException
.
Esto se debe a que sin una referencia a AssemblyA
en AssemblyC
no podría:
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 comprueba y lanza.
Entonces, para calificar de manera segura el conjunto de resultados para los tipos cargables, de acuerdo con este artículo de Phil Haacked Obtenga todos los tipos en una Asamblea y el código de Jon Skeet , en lugar de eso, 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();
}
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 reescribir:
foreach (Type t in this.GetType().Assembly.GetTypes())
if (t is IMyInterface)
; //do stuff
Edición: Acabo de ver la edición para aclarar que la pregunta original era para la reducción de iteraciones / código y que todo está bien como ejercicio, pero en situaciones reales, querrá la implementación más rápida, independientemente de lo genial que se ve el LINQ subyacente.
Aquí está mi método de Utils para iterar a través de los tipos cargados. Maneja clases regulares así como interfaces, y la opción excludeSystemTypes acelera enormemente las cosas si está buscando implementaciones en su propio código 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.
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.
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);
Esto funcionó para mí. Recorre las clases y comprueba si se han generado desde myInterface
foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
.Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
//do stuff
}
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 de 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 el IMyInterface en el ensamblaje MyAssembly
Otras respuestas aquí usan IsAssignableFrom
. También puede usar FindInterfaces
desde el espacio de nombres del System
, como se describe here .
Este es un ejemplo que verifica todos los ensamblajes en la carpeta del ensamblaje que se está ejecutando actualmente, buscando clases que implementen una interfaz determinada (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.
Otras respuestas no funcionaban con una interfaz genérica .
Éste 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();
Asi que con
AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
conseguimos todos los montajes
!x.IsInterface && !x.IsAbstract
Se utiliza para excluir la interfaz y los abstractos y
.Select(x => x.Name).ToList();
tenerlos en una lista.
Para encontrar todos los tipos en un conjunto que implementa 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. Se devolverán 0 tipos. No puedes escribir
where type is IFoo
porque type es una instancia de System.Type, y nunca será de tipo IFoo. En su lugar, verifica si IFoo es asignable del tipo. Que 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 el tiempo de ejecución, verá que regresan 0 tipos, porque todas las instancias de System.Type no eran implementadores de IFoo.
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 en realidad, ¿es eso más legible?
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;
}
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
}
}
}