I'm trying to add generic service support to our IoC container, and I have a question.
Let's say I have the following type:
public interface IService<T> { ... }
public class Service<T> : IService<T> { ... }
and then the following code:
Type type = typeof(Service<>); // <-- notice no specific type here
ConstructorInfo ctor = LogicToFindCtor(type); // <-- picks one of the ctors
Now, having this, I have a generic type, and a constructor that I cannot call, since it was obtained through a generic type that has generic arguments present.
So, given that in my code I'm attempting to resolve a specific type, say IService<Employee>
, I can easily find through my code that I need to resolve this using the concrete type Service<T>
, and that I need to inject the type Employee
in for T
.
However, my question is this: Given that I have the ctor that I want to call already worked out on the generic non-specific type, how do I find the right constructor on the specific type.
Type specificType = typeof(Service<Employee>);
ConstructorInfo specificCtor = MapGenericToSpecific(ctor, specificType);
Edit: I noticed that the property MetaDataToken on both the constructors (the one from the non-specific type and the one from the specific type) has the same value here, can I use this to match? It would still involve a loop to find the right constructor, but one loop with a simple if-statement to find the right one is far preferrable to looking at arguments and attempting to match this way. I've changed the code below. This now compiles and runs, is there a better way with even less reflection, perhaps a direct lookup in the constructor table to find the constructor based on the metadatatoken value?
Here's the code:
using System;
using System.Linq;
using System.Reflection;
using System.Diagnostics;
namespace ConsoleApplication10
{
public class ActivationAttribute : Attribute { }
public class TestClass<T1, T2>
{
public TestClass(String p1)
{
Console.Out.WriteLine("Wrong constructor");
}
[Activation]
public TestClass(T1 p1)
{
Console.Out.WriteLine("Right constructor, p1=" + p1);
}
public TestClass(T2 p2)
{
Console.Out.WriteLine("Wrong constructor");
}
public TestClass()
{
Console.Out.WriteLine("Wrong constructor");
}
public TestClass(T1 p1, T2 p2)
{
Console.Out.WriteLine("Wrong constructor");
}
public TestClass(String p1, T2 p2)
{
Console.Out.WriteLine("Wrong constructor");
}
public TestClass(String p1, Int32 p2)
{
Console.Out.WriteLine("Wrong constructor");
}
}
public class Program
{
static void Main(string[] args)
{
// This is the type I have in my IoC container
Type genericType = typeof(TestClass<,>);
// This is the constructor I locate
ConstructorInfo genericCtor =
(from ctor in genericType.GetConstructors()
where ctor.IsDefined(typeof(ActivationAttribute), false)
select ctor).First();
Debug.Assert(genericCtor != null);
// RESOLUTION STEP
// Upon resolution, two actual types are specified
Type[] genericArguments = new Type[] { typeof(String), typeof(Int32) };
// So I create the actual type from the generic one
Type specificType = genericType.MakeGenericType(genericArguments);
// Can I look up the "genericCtor.MetadataToken" property directly?
// or is this as good as it gets?
ConstructorInfo specificCtor =
(from ctor in specificType.GetConstructors()
where ctor.MetadataToken == genericCtor.MetadataToken
select ctor).First();
Debug.Assert(specificCtor != null);
Debug.Assert(specificCtor != null, "No matching constructors was found");
Object instance = specificCtor.Invoke(new Object[] { "Test" });
Console.Out.Write("Press enter to exit...");
Console.In.ReadLine();
}
}
}