简体   繁体   中英

Can I use MEF to compose several items of the same type with different constructor arguments?

I have the following interface defined

  • IOne
  • ITwo
  • IThree
  • ICalcalculator

And have several implementations of them

  • ClassOne: IOne
  • ClassTwo: IOne
  • ClassThree : ITwo
  • ClassFour : ITwo
  • ClassFive: IThree
  • MyCalc : ICalculator

MyCalc requires classes 1-4, however 2 instances of ClassOne and ClassThree differentiated as initialization ie

public MyCalc(){
    ClassOne first= new ClassOne("filePath1");
    ClassOne second = new ClassOne("filePath2");
    ClassThree third = new ClassThree ("filePath3");
    ClassThree fourth = new ClassThree ("filePath4");
}

I am trying to use MEF to create construct MyCalc. In the above example, the filePathX will be in a configuration file. I have done the following thus far and it seems to work, however my feeling is that my current method and approach is not correct. Looking at this approach, I have bound myself with names (ValDatePrices & EDDatePrices) and it is no cleaner than my current approach(see above).

Is there a cleaner way to load several objects of the same type with different ctor arguments?

public class MyCalc: ICalculator
{
    private CompositionContainer container; 

    public MyCalc()
    {
        var catalog = new AggregateCatalog();
        catalog.Catalogs.Add(new AssemblyCatalog(typeof(MyCalc).Assembly));

        container = new CompositionContainer(catalog);

        try
        {

        this.container.ComposeExportedValue<ClassFive>(
               new ClassFive((ConfigurationManager.AppSettings["SomePath"])));
        this.container.ComposeExportedValue<ClassOne>(
              "ValDatePrices"
              , new ClassOne((ConfigurationManager.AppSettings["filePath1"])));
        this.container.ComposeExportedValue<ClassOne>(
              "EDDatePrices"
               , new ClassOne((ConfigurationManager.AppSettings["filePath2"])));

        this.container.ComposeParts(this);

        }
        catch (CompositionException compositionException)
        {
            Console.WriteLine(compositionException.ToString());
        }

    }

    [Import("ValDatePrices")]
    public ClassOne ValDatePrices;

    [Import("EDDatePrices")]
    public ClassOne EDDatePrices;

    [Import]
    public ClassFive SPointReader;

    public void Calculate()
    {

        Console.WriteLine(SPointReader.Result);
        Console.WriteLine(ValDatePrices.Result.Count);
        Console.WriteLine(EDDatePrices.Result.Count);

        Console.ReadKey(); 
    }
}

Usage

class Program
{
    static void Main(string[] args)
    {
        var p = new MyCalc();
        p.Calculate();
    }
}
}

Side question: where should the code in the MyCalc constructor be located?

Have you considered using a proper DI/IoC container? This can be easily achieved in a clean way using eg NInject or StructureMap .

Here is an example of how this can be implemented with NInject. Note that there are numerous ways of doing this, you should look at their tutorial to get NInject to suit you needs.

MyCalculator

public class MyCalculator : ICalculator
{
    private IOne _oneFirst;
    private IOne _oneSecond;
    private ITwo _twoFirst;
    private ITwo _twoSecond;
    private IThree _three;


    public MyCalculator([Named("Val")] IOne oneFirst, [Named("ED")] IOne oneSecond,
                        [Named("Val")] ITwo twoFirst, [Named("ED")] ITwo twoSecond, IThree three)
    {
        _oneFirst = oneFirst;
        _oneSecond = oneSecond;
        _twoFirst = twoFirst;
        _twoSecond = twoSecond;
        _three = three;

    }

    public void Calculate()
    {

    }
}

Kernel

public class CalculatorModule : NinjectModule
{
    public override void Load()
    {
        Bind<IOne>().ToMethod(CreateOne).Named("Val");
        Bind<IOne>().ToMethod(CreateTwo).Named("ED");
        Bind<ITwo>().ToMethod(CreateThree).Named("Val");
        Bind<ITwo>().ToMethod(CreateFour).Named("ED");
        Bind<IThree>().To<ClassFive>();
        Bind<ICalculator>().To<MyCalculator>();

    }

    private ITwo CreateFour(IContext arg)
    {
        return new ClassFour();
    }

    private ITwo CreateThree(IContext arg)
    {
        return new ClassThree();
    }

    private IOne CreateOne(IContext context)
    {
        return new ClassOne("filePath1");
    }

    private IOne CreateTwo(IContext arg)
    {
        return new ClassTwo("filePath2");
    }


}

Note about kernel: The methods can be shortened to lambda functions if you like and the named attributes can be replaced by parameter names or custom attributes. See Contextual Binding

Usage

class Program
{
    static void Main(string[] args)
    {
        StandardKernel kernel = new StandardKernel(new CalculatorModule());
        var cal = kernel.Get<ICalculator>();
        cal.Calculate();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM