【发布时间】:2015-02-25 22:43:49
【问题描述】:
这只是 .NET 的另一天。直到我不得不使用反射进行序列化来获取具有泛型参数的静态类的泛型方法。听起来没那么糟糕。 GetRuntimeMethod("x", new[] { type }),像往常一样应该做到这一点,或者我是这么想的。
现在,对于以下变体,此方法不断返回 null:
public static Surrogate<T> BuildSurrogate<T>(List<T> collection)。
所以,快速复制到 LinqPad,稍后运行 GetRuntimeMethods,它似乎具有预期的所有方法。自然地,我想,GetRuntimeMethod 的行为可能有些不对劲,所以,我创建了一个快速扩展,GetRuntimeMethodEx 进行迭代,令我惊讶的是,它失败了。什么?怎么会失败。 GetRuntimeMethods 具有我需要的确切方法信息。
所以,我最终将扩展分解为多个部分,以了解究竟发生了什么,如下面的代码所示。事实证明(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。多么迷人!
这是两者之间差异的快速转储,这是相关的。
请注意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;
}
}
【问题讨论】:
-
这失败是合乎逻辑的,永远不要在你的代码中使用
MakeGenericMethod将类型参数 T 绑定到类型参数 -
没有可以使用
MakeGenericMethod绑定的类型,因为它仍然是List<T>和未知的T。 -
@Bas - 你不能绑定未知数!
Edit:@Marcin 打败了我。是的,这就是问题所在。 -
抱歉,我看错了。但是,它不能与
List<>一起使用,因为参数需要是List<T>,这是一种不同的类型。您不能使用相等比较来比较您的“占位符”List<>和List<T>。使用答案中描述的方法 -
得到了答案,正如@Marcin 所述,但是,这很令人困惑,因为这两种类型似乎都显示(几乎)相同的元数据!上图展示了这一点。一个透明的
GenericTypeDefinition (List<>)可以彻底解决它。无论如何,感谢您的快速解决。干杯。 :)
标签: c# .net generics reflection type-equivalence