泛型乐趣:其中typeof(List <t>)!= typeof(List <t>),并使用Reflection获取泛型方法,使用泛型参数</t> </t>

时间:2015-02-25 22:43:49

标签: c# .net generics reflection type-equivalence

这只是.NET的另一天。直到我必须使用泛型参数获取静态类的泛型方法,使用反射进行序列化。听起来不是那么糟糕。 GetRuntimeMethod("x", new[] { type }),像往常一样应该做到这一点,或者我想。

现在,此方法为以下变量保持返回null: public static Surrogate<T> BuildSurrogate<T>(List<T> collection)

因此,快速复制到LinqPad,然后运行GetRuntimeMethods,它似乎拥有所有预期的方法。当然,我想也许,GetRuntimeMethod的行为有些不对劲,所以,我掀起了一个快速扩展,GetRuntimeMethodEx迭代,令我惊讶的是,它失败了。什么?怎么可能失败。 GetRuntimeMethods具有我需要的methodInfo。

所以,我最终将扩展分解成部分以了解究竟发生了什么,如下面的代码所示。事实证明(cType != cGivenType)总是最终为真。

但快速检查显示它们是相同的“明显”类型 - List<T>。现在完全混淆了两个typeof(List<T>)转储的差异,事实证明它们不一样!

两者中的MetadataToken完全相同。然而,RuntimeTypeHandle是不同的。给定类型具有属于AssemblyQualifiedName的正确FullNameSystem.Collections.Generic.List``1, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089。大。但奇怪的是,泛型方法的类型,它们都是“null”!所以,基本上,List<T>是一个神奇的类型,没有相应的程序集(!?)。 It exists, but doesn't。多么迷人!

这是两者之间差异的快速转储,这是相关的。

Image of the differences

注意GenericTypeParametersIsGenericTypeDefinition - 它们似乎很有意义。除了奇怪之外,现在如何在MethodInfo上创建一个匹配此类型的Type?潜在地,编译器期望通用类型List<>具有泛型参数T - 唯一的问题是,您不能用T逐字地创建泛型类型。 T必须是某种东西,现在使相等无效。

 private void Main()
    {
        var type = typeof (List<>);
        var m = typeof (Builders).GetRuntimeMethods();
        var surrogateBuilder = typeof (Builders)
                        .GetRuntimeMethodEx("BuildSurrogate", new[] {type});
    }

    static class Builders
    {
        public static Surrogate<T> BuildSurrogate<T>(List<T> collection)
        {
            return new Surrogate<T>
            {
                Items = collection.ToArray(),
            };
        }

        public class Surrogate<T>
        {
            public IEnumerable<T> Items;
        }
    }

    public static class ReflectionExtensions
    {
        public static MethodInfo GetRuntimeMethodEx(
                this Type type, string name, params Type[] types)
        {
            var m = type.GetRuntimeMethods();
            var res = (m.Where(t =>
            {
                var n = name;
                return t.Name.Equals(n);
            }).FirstOrDefault(t =>
            {
                var px = t.GetParameters().ToArray();
                var currentTypes = px.Select(p => p.ParameterType).ToArray();
                if (currentTypes.Length < 1) return false;
                for (var i = 0; i < types.Length; i++)
                {
                    var cGivenType = types[i];
                    for (var j = 0; j < currentTypes.Length; j++)
                    {
                        var cType = currentTypes[j];
                        if (cType != cGivenType) return false;
                    }
                }
                return true;
            }));
            return res;
        }
    }

1 个答案:

答案 0 :(得分:3)

这是因为您的类型是GenericTypeDefinitionList<>),而且反映您的课程的类型是实际的List<T>

您的代码无法读取,因此我从头开始编写自己的代码。重要的部分是TypesMatch方法。

public static MethodInfo GetRuntimeMethodEx(
        this Type type, string name, params Type[] types)
{
    var withMatchingParamTypes =
        from m in type.GetRuntimeMethods()
        where m.Name == name
        let parameterTypes = m.GetParameters().Select(p => p.ParameterType).ToArray()
        where parameterTypes.Length == types.Length
        let pairs = parameterTypes.Zip(types, (actual, expected) => new {actual, expected})
        where pairs.All(x => TypesMatch(x.actual, x.expected))
        select m;

    return withMatchingParamTypes.FirstOrDefault();
}

private static bool TypesMatch(Type actual, Type expected)
{
    if (actual == expected)
        return true;

    if (actual.IsGenericType && expected.IsGenericTypeDefinition)
        return actual.GetGenericTypeDefinition() == expected;

    return false;
}

按预期返回您的方法。

您无法创建代表Type且未知List<T>的{​​{1}}个实例。这是TGetGenericTypeDefinition的用途。