簡體   English   中英

泛型樂趣:哪里有類型(列表 <T> )!= typeof(列表 <T> ),並使用Reflection獲取通用參數的泛型方法

[英]Generics Fun: Where typeof(List<T>) != typeof(List<T>), and using Reflection to get a generic method, with generic parameters

這只是.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是不同的。 給定類型具有正確的AssemblyQualifiedNameFullName ,屬於System.Collections.Generic.List``1, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 大。 但奇怪的是,泛型方法的類型,它們都是“ null ”! 所以,基本上, List<T>是一個神奇的類型,沒有相應的程序集(!?)。 It exists, but doesn't 多么迷人!

這是兩者之間的差異的快速轉儲,這是相關的。

形象的差異

注意GenericTypeParametersIsGenericTypeDefinition - 它們似乎很有意義。 除了奇怪之外,現在如何在MethodInfo上創建一個匹配此類型的Type? 潛在地,編譯器期望具有泛型參數T的泛型類型List<> - 唯一的問題是,您不能逐字地使用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;
        }
    }

那是因為你的類型是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;
}

按預期返回您的方法。

您無法創建表示具有未知T List<T>Type實例。 這就是GetGenericTypeDefinitionList<>的用途。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM