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.