c# - Generics Fun : Where typeof(List<T>) ! = typeof(List<T>),并使用反射获取泛型方法,带有泛型参数

标签 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 , 和 FullName , 属于 System.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

注意 GenericTypeParameters , 和 IsGenericTypeDefinition - 他们是那些似乎完全有道理的人。除了奇怪之处,现在如何在 MethodInfo 上创建与此类型匹配的类型?潜在地,编译器需要一个泛型类型 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;
        }
    }

最佳答案

那是因为你的类型是 GenericTypeDefinition ( List<> ) 而从反射(reflect)你的类(class)中得到的那个是实际的 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> 的实例未知 T .这就是GetGenericTypeDefinitionList<>是为了。

关于c# - Generics Fun : Where typeof(List<T>) ! = typeof(List<T>),并使用反射获取泛型方法,带有泛型参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28731094/

相关文章:

c# - 如何在 ASP.NET MVC 中使用自定义属性扩展身份?

c# - 如何确定 ASP.NET MVC Post 中使用的编码?

.net - 如何检查数字是否为 .NET 中的整数?

c# - 从抽象父类调用泛型列表上的方法

c# - 如何制作具有实现两个接口(interface)的类型约束的通用集合?

java - 有界类型参数 @see 和 @link javadoc

c# - 对基类的派生类进行序列化和反序列化

c# - OnContextItemSelected 异步调用

.net - Visual Studio 2015 C# MVC::如何获取我的应用程序的基本 URL?

c# - 在 LINQPad 中托管基于 SwaggerUI 的服务