views:

442

answers:

2

Hi there!

I am pretty new to the whole DI/IoC thing, so bear with me...

I have this kind of setting:

interface IA
interface IB
interface IC
abstract class A : IA
class B : A, IB
class C : A, IC

interface IX
interface IY
interface IZ
abstract class X : IX
class Y : X, IY
class Z : X, IZ

B and C's constructors look like this:

public B(IY y);
public C(IZ z);

Now I want either B or C to be constructed, based on an already created instance of either Y or Z. Like this:

IX x = new ...; // either Y or Z, determined at runtime
// lots of code
IA a = fancyfuncoftruth<IA>(x); // creates an instance of either B or C, depending on x

Is something like this possible?

To give you a bit of background: I am trying to combine WPF's treeview, the MVVM pattern and DI.

Thanks for your time.

A: 

I'm not quite sure if I understand what you are looking for, but it seems to me that you are asking whether there's any functionality that can correctly resolve IA based on a specific value of IX (x).

You would be best off implementing this using an Abstract Factory that maps instances of IX to IA.

I'd personally implement this as a custom Abstract Factory, but you can also use UsingFactory or UsingFactoryMethod of Castle Windsor:

IX x = new ...;

var container = new WindsorContainer();
container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<IA>().UsingFactoryMethod(k =>
    {
        // Do fancy stuff with x here
        // This example just shows that x can be referenced
        // in the closure, but I'm not using it...
        if (x == null)
        {
        }
        return k.Resolve<B>();
    }));
container.Register(Component.For<B>());
container.Register(Component.For<IY>().ImplementedBy<Y>());

var result = container.Resolve<IA>();
Mark Seemann
Thanks for your input and sorry for the delay. I have been sick. :-)Anyway, if I understand your solution correctly, I would still have to determine the (actual) type of x at runtime (through GetType() or typeof()) and basically have a big switch which generates the right entity based on this. This kinda smells. ;-)I did a bit more research on the topic and found out that what I primarily need would be something like multimethods/single dispatch for C#, not for methods, but for constructors. I can't really get my head around this (yet).Still, thanks for your help!
Maximilian Csuk
Don't take my answer as authoritative, because I don't think I really understand what it is that you are trying to accomplish. In general, having big switch statements based on types is exactly the sort of thing that DI Containers are intended to avoid. Can you perhaps elaborate a bit on your issue?
Mark Seemann
A: 

Pfeh, I found an answer. Probably not the best, but at least something to start.

Have a look at the following complete example: (I used NInject for this):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject.Core;
using Ninject.Core.Parameters;
using Ninject.Conditions;

namespace IoCTest01
{
    interface IA { }
    interface IB : IA { }
    interface IC : IA { }

    abstract class A : IA { }

    class B : A, IB
    {
        public B(IY x)
        {
            Console.WriteLine("Constructor for B called!");
        }
    }

    class C : A, IC
    {
        public C(IZ x)
        {
            Console.WriteLine("Constructor for C called!");
        }
    }

    interface IX { }
    interface IY : IX { }
    interface IZ : IX { }

    abstract class X : IX { }

    class Y : X, IY
    {
    }
    class Z : X, IZ
    {
    }

    class TestModule : StandardModule
    {
        public override void Load()
        {
            Bind<IY>().To<Y>();
            Bind<IZ>().To<Z>();

            Bind<IA>().To<B>().Only(When.Context.Parameter<ConstructorArgumentParameter>("x").Matches(
                e =>
                {
                    return e.Value.GetType().Equals(typeof(Y));
                }));
            Bind<IA>().To<C>().Only(When.Context.Parameter<ConstructorArgumentParameter>("x").Matches(
                e =>
                {
                    return e.Value.GetType().Equals(typeof(Z));
                }));
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IKernel kernel = new StandardKernel(new TestModule());

            IX x1 = kernel.Get<IY>();
            IX x2 = kernel.Get<IZ>();

            kernel.Dispose();

            // lots of code

            kernel = new StandardKernel(new TestModule());

            var parameters = new ParameterCollection();
            parameters.Add<ConstructorArgumentParameter>(new ConstructorArgumentParameter("x", x1));
            kernel.Get<IA>(parameters);

            parameters = new ParameterCollection();
            parameters.Add<ConstructorArgumentParameter>(new ConstructorArgumentParameter("x", x2));
            kernel.Get<IA>(parameters);
        }
    }
}

When it is run, it displays:

Constructor for B called!
Constructor for C called!

First, I had to manually inject the already instantiated objects x1 and x2 into the kernel.Get-calls. This looked like it was enough to let NInject resolve the right entity, but as soon as I added a second Binding for IA, it complained about multiple default bindings for IA. So I had to do some contextual binding:

Bind<IA>().To<B>().Only(When.Context.Parameter<ConstructorArgumentParameter>("x").Matches(
                    e =>
                    {
                        return e.Value.GetType().Equals(typeof(Y));
                    }));

This checks if the parameter x is of type Y. If yes, this binding is used.

Again, while this is a solution, it's probably far from being optimal. I wished NInject could resolve the right type (B or C) to instantiate from the dynamic type (Y or Z) of the parameter given (x).

Ah well. :-)

Does anybody have a better solution?

To Mark: does the code explain the problem better? The Main-method should give you an overview.

Maximilian Csuk