泛型乐趣:其中 typeof(List<T>) != typeof(List<T>),并使用反射获得具有泛型参数的泛型方法
Generics Fun: Where typeof(List<T>) != typeof(List<T>), and using Reflection to get a generic method, with generic parameters
这只是 .NET 的又一天。直到我不得不使用反射进行序列化,获得带有泛型参数的静态 class 的泛型方法。听起来还不错。 GetRuntimeMethod("x", new[] { type })
,像往常一样应该可以解决问题,或者我是这么想的。
现在,此方法不断为以下变体返回 null:
public static Surrogate<T> BuildSurrogate<T>(List<T> collection)
。
所以,快速复制到 LinqPad,然后 GetRuntimeMethods
运行,它似乎具有预期的所有方法。很自然地,我想也许 GetRuntimeMethod 的行为有些不对,所以,我快速扩展了 GetRuntimeMethodEx
来迭代,令我惊讶的是,它失败了。什么?怎么会失败。 GetRuntimeMethods 具有我需要的确切 methodInfo。
因此,我最终将扩展程序分解成多个部分以了解到底发生了什么,如下面的代码所示。事实证明 (cType != cGivenType)
总是以 true 结束。
但快速检查显示它们是相同的 'apparent' 类型 - List<T>
。现在完全糊涂了,对两个 typeof(List<T>)
的转储进行比较,结果发现它们不一样!
两者的MetadataToken
完全一样。然而 RuntimeTypeHandle
是不同的。给定的类型具有正确的 AssemblyQualifiedName
和属于 System.Collections.Generic.List``1, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
的 FullName
。伟大的。但奇怪的是,泛型方法的类型,它们都是“null
”!所以,基本上,List<T>
是一个没有对应程序集(!?)的魔法类型。 It exists, but doesn't
。多么迷人!
这是两者之间差异的快速转储,这是相关的。
请注意 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<>
) 而反映你的 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;
}
Returns 你的方法,符合预期。
您无法创建代表 List<T>
且 T
未知的 Type
实例。这就是 GetGenericTypeDefinition
和 List<>
的目的。
这只是 .NET 的又一天。直到我不得不使用反射进行序列化,获得带有泛型参数的静态 class 的泛型方法。听起来还不错。 GetRuntimeMethod("x", new[] { type })
,像往常一样应该可以解决问题,或者我是这么想的。
现在,此方法不断为以下变体返回 null:
public static Surrogate<T> BuildSurrogate<T>(List<T> collection)
。
所以,快速复制到 LinqPad,然后 GetRuntimeMethods
运行,它似乎具有预期的所有方法。很自然地,我想也许 GetRuntimeMethod 的行为有些不对,所以,我快速扩展了 GetRuntimeMethodEx
来迭代,令我惊讶的是,它失败了。什么?怎么会失败。 GetRuntimeMethods 具有我需要的确切 methodInfo。
因此,我最终将扩展程序分解成多个部分以了解到底发生了什么,如下面的代码所示。事实证明 (cType != cGivenType)
总是以 true 结束。
但快速检查显示它们是相同的 'apparent' 类型 - List<T>
。现在完全糊涂了,对两个 typeof(List<T>)
的转储进行比较,结果发现它们不一样!
两者的MetadataToken
完全一样。然而 RuntimeTypeHandle
是不同的。给定的类型具有正确的 AssemblyQualifiedName
和属于 System.Collections.Generic.List``1, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
的 FullName
。伟大的。但奇怪的是,泛型方法的类型,它们都是“null
”!所以,基本上,List<T>
是一个没有对应程序集(!?)的魔法类型。 It exists, but doesn't
。多么迷人!
这是两者之间差异的快速转储,这是相关的。
请注意 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<>
) 而反映你的 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;
}
Returns 你的方法,符合预期。
您无法创建代表 List<T>
且 T
未知的 Type
实例。这就是 GetGenericTypeDefinition
和 List<>
的目的。