2009-06-22 12 views
5

Ho bisogno di aggiungere alcuni punti di estensione al nostro codice esistente, e ho cercato MEF come una possibile soluzione. Abbiamo un'interfaccia IRandomNumberGenerator, con un'implementazione predefinita (ConcreteRNG) che vorremmo poter essere scambiati. Questo suona come uno scenario ideale per MEF, ma ho avuto problemi con il modo in cui istanziamo i generatori di numeri casuali. Il nostro codice attuale assomiglia:Posso controllare la creazione dell'oggetto tramite MEF?

public class Consumer 
{ 
    private List<IRandomNumberGenerator> generators; 
    private List<double> seeds; 

    public Consumer() 
    { 
     generators = new List<IRandomNumberGenerator>(); 
     seeds = new List<double>(new[] {1.0, 2.0, 3.0}); 

     foreach(var seed in seeds) 
     { 
      generators.Add(new ConcreteRNG(seed)); 
     } 
    } 
} 

In altre parole, il consumatore è responsabile per istanziare i RNG di cui ha bisogno, tra cui fornire il seme che ogni istanza richiede.

Quello che mi piacerebbe fare è scoprire l'implementazione concreta di RNG e l'istanza di MEF (usando DirectoryCatalog). Non sono sicuro di come farlo. Potrei esporre una proprietà di Generators e contrassegnarla come [Import], ma come posso fornire i semi richiesti?

C'è qualche altro approccio che mi manca?

risposta

5

Attualmente non esiste un modo diretto per farlo in MEF ma il team MEF sta prendendo in considerazione il supporto per questo in v.Next. In sostanza, vuoi creare più istanze della stessa implementazione che è tradizionalmente fatta usando un modello di fabbrica. Così un approccio si potrebbe usare è qualcosa di simile:

public interface IRandomNumberGeneratorFactory 
{ 
    IRandomNumberGenerator CreateGenerator(int seed); 
} 

[Export(typeof(IRandomNumberGeneratorFactory))] 
public class ConcreateRNGFactory : IRandomNumberGeneratorFactory 
{ 
    public IRandomNumberGenerator CreateGenerator(int seed) 
    { 
    return new ConcreateRNG(seed); 
    } 
} 

public class Consumer 
{ 
    [Import(typeof(IRandomNumberGeneratorFactory))] 
    private IRandomNumberGeneratorFactory generatorFactory; 
    private List<IRandomNumberGenerator> generators;  
    private List<double> seeds;  

    public Consumer()  
    { 
    generators = new List<IRandomNumberGenerator>(); 
    seeds = new List<double>(new[] {1.0, 2.0, 3.0}); 

    foreach(var seed in seeds) 
    {    
     generators.Add(generatorFactory.CreateGenerator(seed)); 
    } 
    } 
} 
+0

Grazie Wes. Avevo preso in considerazione un approccio di fabbrica, ma ero rimasto bloccato a causa del fatto che volevo uno stabilimento generico in grado di creare un'istanza di qualsiasi tipo di IRandomNumberGenerator scoperto da MEF. Ripensandoci, il tuo approccio non sembra molto di più, grazie ancora. – Akash

+1

Ora funziona. L'ho semplificato un po 'fornendo un metodo factory statico su ConcreteRNG: [Esporta (typeof (Func ))] public static solo in lettura Func Create = seed => new ConcreteRNG (seed) ; – Akash

+0

Sì, esportare una funzione è anche un altro modo semplificato per ottenere ciò che si desidera. Inoltre, mi sono appena reso conto che se si desidera utilizzare quell'importazione all'interno del costruttore, sarà necessario importarlo come costruttore, poiché l'importazione come dimostrata non sarebbe stata impostata prima della costruzione dell'oggetto. –

0

Credo che questo è ciò che la caratteristica Lazy Exports è per. Da quella pagina:

[Import] 
public Export<IMessageSender> Sender { get; set; } 

In questo caso si è opt-in per ritardare questo esemplificazione fino a quando effettivamente necessario l'istanza di attuazione. Per richiedere l'istanza, utilizzare il metodo [Export.GetExportedObject()]. Si noti che questo metodo non agirà mai come una fabbrica di implementazioni di T, quindi chiamandolo più volte restituirà la stessa istanza dell'oggetto restituita alla prima chiamata.

+0

Scott, ho bisogno di più istanze di IRandomNumberGenerator. Il tuo commento suggerisce che otterrò sempre la stessa istanza. Mi sto perdendo qualcosa? – Akash

+0

Mi dispiace, ho perso quella parte. In quel caso penso che tu abbia bisogno del modello di fabbrica. –

4

MEF preview 8 ha un supporto sperimentale per questo, sebbene non sia ancora incluso in System.ComponentModel.Composition.dll. Vedere this blog post per ulteriori informazioni.

Dovrai scaricare i sorgenti MEF e creare la soluzione. Nella cartella Samples\DynamicInstantiation troverai l'assemblaggio Microsoft.ComponentModel.Composition.DynamicInstantiation.dll. Aggiungere un riferimento a questa assemblea e aggiungere un provider di un'istanza dinamica per il vostro contenitore in questo modo:

var catalog = new DirectoryCatalog("."); 
var dynamicInstantiationProvider = new DynamicInstantiationProvider(); 
var container = new CompositionContainer(catalog, dynamicInstantiationProvider); 
dynamicInstantiationProvider.SourceProvider = container; 

Ora vostre parti saranno in grado di importare una PartCreator<Foo> se hanno bisogno di creare dinamicamente Foo parti. Il vantaggio rispetto alla scrittura della propria classe di produzione è che questo si occuperà in modo trasparente delle importazioni di Foo e delle importazioni "importate", eccetera.

modificare:

  • in MEF Preview 9PartCreator è stato rinominato per ExportFactory ma è incluso solo nella versione Silverlight.
  • in MEF 2 Preview 2, ExportFactory è diventato incluso per l'edizione desktop. Quindi, ExportFactory sarà probabilmente parte della prossima versione di .NET framework dopo .NET 4.0.
+0

Grazie, seguirò su quel collegamento. – Akash