这只是 .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
.多么迷人!
这是两者之间差异的快速转储,这是相关的。
注意 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
.这就是GetGenericTypeDefinition
和 List<>
是为了。
关于c# - Generics Fun : Where typeof(List<T>) ! = typeof(List<T>),并使用反射获取泛型方法,带有泛型参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28731094/