2011-02-25 12 views
17

Tengo una interfaz IExample, y un conjunto de clases ClassOne, ClassTwo y ClassThree, todas definidas en diferentes espacios de nombres. Posiblemente eliminaré cualquiera de las clases, o agregaré una nueva en un lugar nuevo, en una etapa posterior de desarrollo.Crear una instancia de todas las clases implementando una interfaz específica

Ahora, quiero encontrar todos los tipos que implementan IExample en tiempo de ejecución, y ejemplificarlos. (Sé de antemano que ninguna clase que implementa IExample necesitará siempre alguna argumentos de constructor, pero no sé cómo especificar que en el código, por lo que soy yo - no el compilador - que sabe ...)

¿Este ¿posible? ¿Cómo voy a hacerlo?

Actualización: ahora he probado varios de los enfoques sugeridos, pero en todos ellos, la línea Activator.CreateInstance(type), consigo un System.MissingMethodException porque "no se puede crear una instancia de una interfaz." Este es mi código:

var tasks = AppDomain.CurrentDomain.GetAssemblies() 
    .SelectMany(a => a.GetTypes()) 
    .Where(t => typeof(IBootstrapperTask).IsAssignableFrom(t)) 

    // This line is where it fails 
    .Select(t => Activator.CreateInstance(t) as IBootstrapperTask) 

    .ToArray(); 
new AutoMapperBootstrapper(tasks).Initialize(); 

Sin la cláusula as no veo ninguna excepción, pero me dio una object[], y necesito un IBootstrapperTask[] para el constructor en la última línea en el extracto. He intentado varias formas de lanzarlo, pero ninguno parece funcionar.

Respuesta

28

Esto se puede hacer con Reflection. Por ejemplo

var interfaceType = typeof(IExample); 
var all = AppDomain.CurrentDomain.GetAssemblies() 
    .SelectMany(x => x.GetTypes()) 
    .Where(x => interfaceType.IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract) 
    .Select(x => Activator.CreateInstance(x)); 

Nota: Esto sólo crear instancias de IExample de ensamblados cargados en la corriente AppDomain. Esto puede ser diferente a todos los ensamblados cargados en el proceso actual. Sin embargo, para muchos tipos de aplicaciones, esto será equivalente.

+4

Más importante aún, solo incluirá ensamblajes que ya hayan sido * cargados *. Puede haber otros que * han sido * referenciados * pero aún no están cargados. Personalmente prefiero mantener todo explícito :) –

0

Intenta usar la reflexión para obtener los tipos que implementan IExample. Este ejemplo examina el ensamblaje actual pero puede pasar fácilmente un ensamblaje diferente:

 IEnumerable<Type> types = Assembly.GetExecutingAssembly() 
      .GetTypes().Where(t=>t.GetInterfaces().Where(tt==typeof(IExample)).Count()>0); 
     List<object> objects = new List<object>(); 
     foreach (Type type in types) 
     { 
      objects.Add(Activator.CreateInstance(type)); 
     } 
+0

¿Por qué no llamas a 'Contains'? Además, eso no manejará la herencia. – SLaks

+1

Esto no funcionará, ya que no puedo crear instancias de interfaces. Debo crear instancias de los tipos que las implementan. –

+0

¡Esto no fallará! Estoy llamando 't.GetInterfaces()' en los tipos. Estoy usando algo similar en un proyecto mío. – Aliostad

8

Se necesitaría conocer una lista de los conjuntos de mirar adentro, pero luego LINQ hace que sea relativamente fácil:

var instances = (from assembly in assemblies 
       from type in assembly 
       where !type.IsAbstract && 
         type.IsClass && 
         type.IsPublic && 
         !type.IsGenericType && 
         typeof(IExample).IsAssignableFrom(type) 
       let ctor = type.GetConstructor(Type.EmptyTypes) 
       where ctor != null && ctor.IsPublic 
       select (IExample) Activator.CreateInstance(type)) 
       .ToList(); 

Es posible pensar en algunas otras restricciones que añadir, pero son bastante fáciles de expreso :)

instances será entonces un List<IExample>.

EDIT: sospecho mi código funcionará donde el tuyo no, porque excluyo específicamente las no clases. Supongo que su código intenta crear una instancia de la interfaz en sí, es decir, cuando t es .

+0

Gracias por su respuesta, por favor consulte mi actualización para obtener más información. –

+0

@Tomas: Editado. ¿Has probado mi solución? (Cambie la última línea a 'ToArray' si lo desea). Es cierto que no puedo ver por qué el' as' podría haber cambiado el código original ... –

+0

Gracias. Cuando miré más de cerca tu código, noté los requisitos explícitos de ser público, no abstracto y con un constructor, y también los implementé en mi código. Ahora bien, esta parte específica funciona, pero tengo otros problemas (posiblemente no relacionados), por lo que no puedo ver si esto realmente funciona, o simplemente arroja una excepción diferente. –

5

¿Necesita que esto suceda de forma dinámica? ¿Alguna vez puedes tener uno que no quieras crear? Me parece que este es un buen caso para la inyección de dependencia (que se puede configurar). Por ejemplo, Unity tiene un método ResolveAll.

Desde el enlace de arriba;

IEnumerable<IMyObject> objects = myContainer.ResolveAll<IMyObject>(); 
+0

Con solo la información de ejemplo limitada que di en mi pregunta, este es un argumento válido, pero en este caso DI no es aplicable. –

+0

Ha - solo porque fui tan rápido en descartar DI, resulta que de todos modos terminaré así ...: P ¡Gracias! Sin embargo, no marcaré esto como la respuesta, simplemente porque realmente no responde la pregunta ("¿cómo puedo encontrar todas las clases que implementan esta interfaz de forma dinámica?"), A pesar de que resuelve mi problema real. –

+0

+1 ¡Muchas gracias! ¡Había estado construyendo un proyecto usando Caliburn.Micro para manejar todas mis cosas relacionadas con MVVM y nunca pensé que podría usarlo para resolver este problema también! Cualquier persona a la que le guste esta solución debería probar este marco. – CuddleBunny

Cuestiones relacionadas