2011-02-25 29 views
17

Ho un'interfaccia IExample e un set di classi ClassOne, ClassTwo e ClassThree, tutte definite in diversi spazi dei nomi. Eliminerò eventualmente una delle due classi o ne aggiungerò una nuova in una nuova posizione, in una fase successiva di sviluppo.Istanziare tutte le classi che implementano un'interfaccia specifica

Ora, voglio trovare tutti i tipi che implementano IExample in fase di runtime e istanziarli. (So ​​a priori che nessuna classe che implementa IExample avrà mai bisogno di alcun argomento del costruttore, ma non so come specificarlo nel codice, quindi sono io - non il compilatore - che sa ...)

È questo possibile? Come faccio a farlo?

Update: "Non posso creare un'istanza di un'interfaccia" Ora ho provato diversi gli approcci suggeriti, ma su tutti, la linea Activator.CreateInstance(type), ottengo uno System.MissingMethodException perché io Questo è il mio codice:

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

senza la clausola as non vedo alcuna eccezione, ma sto dato un object[], e ho bisogno di un IBootstrapperTask[] per il costruttore sull'ultima riga del brano. Ho provato vari modi per lanciarlo, ma nessuno sembra funzionare.

risposta

28

Questo può essere fatto con Reflection. Ad esempio

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: Ciò creerà solo istanze di IExample da assembly caricati nella corrente AppDomain. Questo può essere diverso da tutti gli assembly caricati nel processo corrente. Tuttavia per molti tipi di applicazioni questo sarà equivalente.

+4

Ancora più importante, tuttavia, includerà solo assiemi che sono già stati * caricati *. Ci possono essere altri che sono stati * referenziati * ma non ancora caricati. Personalmente preferisco tenerlo tutto esplicito :) –

0

Provare a usare la reflection per ottenere i tipi di applicazione IExample. Questo esempio esamina l'assembly corrente ma è possibile passare facilmente un assembly diverso:

 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

Perché non si chiama 'Contains'? Inoltre, questo non gestirà l'ereditarietà. – SLaks

+1

Questo fallirà, dal momento che non posso creare istanze di interfacce - Devo creare istanze dei tipi che le implementano. –

+0

Questo non fallirà! Sto chiamando 't.GetInterfaces()' sui tipi. Sto usando qualcosa di simile in un mio progetto. – Aliostad

8

Avresti bisogno di conoscere un elenco di gruppi di guardare, ma poi LINQ rende relativamente facile:

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

Si può pensare di alcune altre restrizioni da aggiungere, ma sono abbastanza facili da esprimono :)

instances sarà quindi un List<IExample>.

EDIT: Sospetto che il codice my funzionerà laddove il tuo no, perché escludo specificatamente le non classi. La mia ipotesi è che il codice stia cercando di creare un'istanza dell'interfaccia stessa, ad esempio quando t è typeof(IBootstrapperTask).

+0

Grazie per la tua risposta - per favore guarda il mio aggiornamento per ulteriori informazioni. –

+0

@Tomas: Modificato. Hai provato la mia soluzione? (Se lo desideri, modifica l'ultima riga di 'ToArray'.) Ammetto che non riesco a capire perché" as "avrebbe fatto alcuna differenza con il tuo codice originale ... –

+0

Grazie. Quando ho guardato più da vicino il tuo codice ho notato i requisiti espliciti di essere pubblico, non astratto e con un costruttore, e li ho implementati anche nel mio codice. Ora questa parte specifica funziona, ma ho altri problemi (probabilmente non correlati), quindi non sono in grado di vedere se questo funziona, o semplicemente lancia una diversa eccezione. –

5

Avete bisogno che questo accada dinamicamente? Ci sono sempre momenti in cui potresti averne uno che non vuoi creare? Mi sembra che questo sia un buon caso per l'iniezione di dipendenza (che può essere configurata). Per esempio Unity ha un metodo ResolveAll.

Dal link precedente;

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

Con solo le informazioni di esempio limitate che ho dato nella mia domanda questo è un argomento valido, ma in questo caso DI non è applicabile. –

+0

Ha - solo perché sono stato così veloce a respingere DI, si scopre che questo è dove finirò comunque ...: P Grazie! Tuttavia, non la contrassegnerò come risposta, semplicemente perché non risponde alla domanda ("come trovo tutte le classi che implementano questa interfaccia in modo dinamico?"), Anche se risolve il mio problema reale. –

+0

+1 Grazie mille! Stavo costruendo un progetto usando Caliburn.Micro per gestire tutte le mie cose relative a MVVM e non mi è mai venuto in mente che avrei potuto usarlo per risolvere anche questo problema! Qualsiasi altro a cui piace questa soluzione dovrebbe provare questo framework. – CuddleBunny

Problemi correlati