C#中的通用类型构造函数解析,适用于具有通用服务注册的IoC

时间:2009-10-19 11:42:24

标签: c# generics reflection

我正在尝试为我们的Io​​C容器添加通用服务支持,我有一个问题。

假设我有以下类型:

public interface IService<T> { ... }
public class Service<T> : IService<T> { ... }

然后是以下代码:

Type type = typeof(Service<>); // <-- notice no specific type here
ConstructorInfo ctor = LogicToFindCtor(type); // <-- picks one of the ctors

现在,有了这个,我有一个泛型类型和一个我无法调用的构造函数,因为它是通过一个存在泛型参数的泛型类型获得的。

所以,鉴于我在我的代码中尝试解析特定类型,比如IService<Employee>,我可以通过我的代码轻松找到我需要使用具体类型Service<T>解决此问题,并且我需要为Employee注入类型T

但是,我的问题是:鉴于我想要调用的ctor已经在泛型非特定类型上得到了解,我如何在特定类型上找到正确的构造函数。

Type specificType = typeof(Service<Employee>);
ConstructorInfo specificCtor = MapGenericToSpecific(ctor, specificType);

编辑:我注意到两个构造函数上的属性MetaDataToken(来自非特定类型的属性和来自特定类型的属性)在这里具有相同的值,可以我用它来匹配?它仍然需要一个循环来找到正确的构造函数,但是一个带有简单if语句的循环来找到正确的构造函数更容易看到参数并尝试匹配这种方式。我已经改变了下面的代码。现在编译并运行,是否有更好的方法,甚至更少的反射,也许在构造函数表中直接查找根据metadatatoken值找到构造函数?

以下是代码:

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();
        }
    }
}

1 个答案:

答案 0 :(得分:2)

您可以依赖 MetadataToken 在有界和无界构造函数上保持相同。元数据是使用IL代码创建的,它是类型的一部分,它与通用参数绑定无关。

.NET不会为每次使用泛型参数创建不同的类型(例如List<int>List<bool>)。它是相同的类型,但在每次调用时,带有方法参数的有限泛型参数传递给堆栈(我知道你可能已经知道所有这些,但我只是确定)。

因此,当类通用参数绑定到特定类型时,附加到该类型的 MetadataToken 在运行时不会更改。例如:

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);

底线, MetadataToken 是IL的“一部分”,并且对于具有不同通用参数绑定的相同类型的实例没有区别。


出于好奇(我可能遗漏了某些东西),为什么不使用在绑定类型上找到 Activation 构造函数的过滤器,就像它在未绑定类型上找到的一样? Activation 属性也会修饰有界构造函数。