2009-06-22 10 views
5

Necesito agregar algunos puntos de extensión a nuestro código existente, y he estado viendo a MEF como una posible solución. Tenemos una interfaz IRandomNumberGenerator, con una implementación predeterminada (ConcreteRNG) que nos gustaría ser intercambiables. Esto suena como un escenario ideal para MEF, pero he estado teniendo problemas con la forma en que instanciamos los generadores de números aleatorios. Nuestro código actual se parece a:¿Puedo controlar la creación de objetos usando 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)); 
     } 
    } 
} 

En otras palabras, el consumidor es responsable de instanciar los generadores de números aleatorios que necesita, incluyendo el suministro de la semilla que cada caso requiere.

Lo que me gustaría hacer es tener la implementación concreta de RNG descubierta e instanciada por MEF (usando DirectoryCatalog). No estoy seguro de cómo lograr esto. Podría exponer una propiedad de Generators y marcarla como [Importar], pero ¿cómo proporciono las semillas requeridas?

¿Hay algún otro enfoque que me falta?

Respuesta

5

Actualmente no hay una manera directa de hacer esto en MEF pero el equipo de MEF está considerando apoyar esto en v.Next. Básicamente, desea crear varias instancias de la misma implementación que se hace tradicionalmente utilizando un patrón de fábrica. Así que un enfoque que podría utilizar es algo así como:

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

Gracias Wes. Había considerado un enfoque de fábrica, pero me había atascado debido al hecho de que quería una fábrica genérica que creara una instancia de cualquier tipo IRandomNumberGenerator que fuera descubierto por MEF. Pensando de nuevo, su enfoque no parece mucho trabajo extra, gracias de nuevo. – Akash

+1

Lo tengo trabajando ahora. Lo simplifiqué un poco al proporcionar un método de fábrica estático en ConcreteRNG: [Exportar (typeof (Func ))] public static readonly Func Create = seed => new ConcreteRNG (seed) ; – Akash

+0

Sí exportar una función en sí misma es también otra forma simplificada de obtener lo que desea. También me acabo de dar cuenta de que si quieres usar esa importación dentro del constructor, entonces necesitarás hacer una importación de constructor, porque esa importación como lo demostré no se establecería antes de la construcción del objeto. –

0

Creo que esto es lo que la función es para Lazy Exports. Desde esa página:

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

En este caso son opt-in para retrasar esta ejemplificación hasta que realmente necesita la instancia de ejecución. Para solicitar la instancia, use el método [Export.GetExportedObject()]. Tenga en cuenta que este método nunca actuará como una fábrica de implementaciones de T, por lo que al llamarlo varias veces se devolverá la misma instancia de objeto devuelta en la primera llamada.

+0

Scott, necesito varias instancias de IRandomNumberGenerator. Tu comentario sugiere que obtendré la misma instancia cada vez. ¿Me estoy perdiendo de algo? – Akash

+0

Lo siento, me perdí esa parte. En ese caso, creo que necesitas el patrón de fábrica. –

4

MEF preview 8 tiene soporte experimental para esto, aunque todavía no está incluido en System.ComponentModel.Composition.dll. Consulte this blog post para obtener más información.

Tendrá que descargar las fuentes de MEF y crear la solución. En la carpeta Samples\DynamicInstantiation, encontrará el ensamblaje Microsoft.ComponentModel.Composition.DynamicInstantiation.dll. Agregue una referencia a esta reunión y de agregar un proveedor de instancias dinámico a su contenedor de la siguiente manera:

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

Ahora sus partes será capaz de importar un PartCreator<Foo> si necesitan para crear dinámicamente Foo partes. La ventaja de escribir su propia clase de fábrica es que esto se ocupará de manera transparente de las importaciones de Foo, y de las importaciones importadas, etcétera.

edición:

  • en MEF Preview 9PartCreator fue renombrado a ExportFactory pero sólo está incluido en la edición de Silverlight.
  • en MEF 2 Preview 2, ExportFactory se incluyeron para la edición de escritorio. Así que ExportFactory probablemente será parte de la próxima versión de .NET Framework después de .NET 4.0.
+0

Gracias, haré un seguimiento en ese enlace. – Akash

Cuestiones relacionadas