views:

177

answers:

1

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();
        }
    }
}
+2  A: 

You can rely on the MetadataToken to remain the same on the bounded and unbounded constructor. The metadata is created with the IL code, it's part of the type and it's not related to the generic arguments binding.

.NET does not create different type for every use of generic argument (e.g. List and List). It is the same type, but on every call the bounded generic arguments passed on stack with the method arguments (I know you may already know all this but I am just making sure).

Therefor, MetadataToken attached to the type doesn't change in runtime when the class generic arguments are bounded to to specific types. For example:

bool unboundAndBoundHasSameMetadataToken =
           typeof(IList<>).MetadataToken ==  typeof(IList<int>).MetadataToken;

bool boundedClassesHasSameMetadataToken =
           typeof(IList<bool>).MetadataToken == typeof(IList<int>).MetadataToken;

Assert.IsTrue(unboundAndBoundHasSameMetadataToken);
Assert.IsTrue(boundedClassesHasSameMetadataToken);

Bottom line, MetadataToken is "part of" the IL and doesn't differ on instances of same type with different generic arguments binding.


Out of curiosity (I am probably missing something), why not use the filter that finds the Activation constructor on the bounded type the same way it's found on the unbound type? The Activation attribute decorates the bounded constructor as well.

Elisha
Please excuse my ignorance, but when you say "bounded and unbounded constructor", what is actually the difference? (I know I'm probably not using the right terms, and yours might probably be the correct ones) Do you mean that the constructor I found for my generic class type, when I still don't know the actual type for T (unbounded), compared to the constructor I need to locate for the specifici type, when I know the type of T? And again, please excuse my lack of using the right terms, as I don't know how to precisely describe what I mean.
Lasse V. Karlsen
... and good call on the [Activation] attribute.... (hit head) Don't know why I didn't think of that...
Lasse V. Karlsen
But, just to get this straight, are you saying that if I inspect the type `SomeGenericType(T)` where I still don't know the type of T, compared with a *specific* type of `SomeGenericType(T)`, methods will have the same MetadataToken value? (ie. the value is not depending on the type T in any way?)
Lasse V. Karlsen
Bounded constructor - the constructor you get when querying the type after T is set to specific type (e.g. typeof(List<int>). GetConstructors())Unbounded constructor - the constructor you get when querying the type before T is set to specific type (e.g. typeof(List<>). GetConstructors())By the way, the correct term is, as you called it in your comment, constructor of type where generic arguments are bounded/unbounded.The MetadataToken does not depend on T, SomeGenericType(T) with specific T has the same MetaData as SomeGenericType(T) before T is specified.
Elisha