我正在尝试为Enumerable类型的Where方法检索MethodInfo:
typeof (Enumerable).GetMethod("Where", new Type[] { typeof(IEnumerable<>), typeof(Func<,>) })
但获取空值。我究竟做错了什么?
但是,先前的答案在某些情况下适用:
Action<IEnumerable<T>>
Action<>
string.Concat(IEnumerable<string>)
string.Concat<T>(IEnumerable<T>)
"Concat"
IEnumerable<>
type.GetMethod()
BindingFlags
type.GetMethods()
GetMethods()
type.GetMember(name, MemberTypes.Method, ...)
GetGenericMethod()
这是解决所有这些问题的版本,可以用作有缺陷的的通用替代品GetMethod()。请注意,提供了两种扩展方法,一种带有BindingFlags,另一种没有(为了方便)。
GetMethod()
/// <summary> /// Search for a method by name and parameter types. /// Unlike GetMethod(), does 'loose' matching on generic /// parameter types, and searches base interfaces. /// </summary> /// <exception cref="AmbiguousMatchException"/> public static MethodInfo GetMethodExt( this Type thisType, string name, params Type[] parameterTypes) { return GetMethodExt(thisType, name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, parameterTypes); } /// <summary> /// Search for a method by name, parameter types, and binding flags. /// Unlike GetMethod(), does 'loose' matching on generic /// parameter types, and searches base interfaces. /// </summary> /// <exception cref="AmbiguousMatchException"/> public static MethodInfo GetMethodExt( this Type thisType, string name, BindingFlags bindingFlags, params Type[] parameterTypes) { MethodInfo matchingMethod = null; // Check all methods with the specified name, including in base classes GetMethodExt(ref matchingMethod, thisType, name, bindingFlags, parameterTypes); // If we're searching an interface, we have to manually search base interfaces if (matchingMethod == null && thisType.IsInterface) { foreach (Type interfaceType in thisType.GetInterfaces()) GetMethodExt(ref matchingMethod, interfaceType, name, bindingFlags, parameterTypes); } return matchingMethod; } private static void GetMethodExt( ref MethodInfo matchingMethod, Type type, string name, BindingFlags bindingFlags, params Type[] parameterTypes) { // Check all methods with the specified name, including in base classes foreach (MethodInfo methodInfo in type.GetMember(name, MemberTypes.Method, bindingFlags)) { // Check that the parameter counts and types match, // with 'loose' matching on generic parameters ParameterInfo[] parameterInfos = methodInfo.GetParameters(); if (parameterInfos.Length == parameterTypes.Length) { int i = 0; for (; i < parameterInfos.Length; ++i) { if (!parameterInfos[i].ParameterType .IsSimilarType(parameterTypes[i])) break; } if (i == parameterInfos.Length) { if (matchingMethod == null) matchingMethod = methodInfo; else throw new AmbiguousMatchException( "More than one matching method found!"); } } } } /// <summary> /// Special type used to match any generic parameter type in GetMethodExt(). /// </summary> public class T { } /// <summary> /// Determines if the two types are either identical, or are both generic /// parameters or generic types with generic parameters in the same /// locations (generic parameters match any other generic paramter, /// but NOT concrete types). /// </summary> private static bool IsSimilarType(this Type thisType, Type type) { // Ignore any 'ref' types if (thisType.IsByRef) thisType = thisType.GetElementType(); if (type.IsByRef) type = type.GetElementType(); // Handle array types if (thisType.IsArray && type.IsArray) return thisType.GetElementType().IsSimilarType(type.GetElementType()); // If the types are identical, or they're both generic parameters // or the special 'T' type, treat as a match if (thisType == type || ((thisType.IsGenericParameter || thisType == typeof(T)) && (type.IsGenericParameter || type == typeof(T)))) return true; // Handle any generic arguments if (thisType.IsGenericType && type.IsGenericType) { Type[] thisArguments = thisType.GetGenericArguments(); Type[] arguments = type.GetGenericArguments(); if (thisArguments.Length == arguments.Length) { for (int i = 0; i < thisArguments.Length; ++i) { if (!thisArguments[i].IsSimilarType(arguments[i])) return false; } return true; } } return false; }
请注意,IsSimilarType(Type)扩展方法可以公开,并且可能单独使用。我知道,这个名字不是很好- 我们欢迎您提出一个更好的名字,但是可能很难解释它的作用。此外,我还通过检查“ ref”和数组类型(引用被忽略以进行匹配,但数组尺寸必须匹配)来添加了另一个改进。
IsSimilarType(Type)
因此,这就是Microsoft 应该 这样做的方式。其实并不难。
是的,我知道,您可以使用Linq缩短某些逻辑,但是我不是像这样的底层例程对Linq的忠实拥护者,并且除非Linq与原始代码一样容易遵循,否则我不会这样做。 IMO常常不是这样。
如果您喜欢Linq,并且必须这样做,则可以用此替换最里面的部分IsSimilarType()(将8行变成1):
IsSimilarType()
if (thisArguments.Length == arguments.Length) return !thisArguments.Where((t, i) => !t.IsSimilarType(arguments[i])).Any();
最后一件事:如果您要查找具有泛型参数的泛型方法,例如Method<T>(T, T[]),则必须找到一个类型为泛型参数(IsGenericParameter == true)的参数类型(任何人都可以这样做,因为“通配符”匹配项)。但是,您不能只是做new Type()-您必须找到一个真正的(或使用TypeBuilder构建一个)。为了简化此过程,我添加了public class T声明,并添加了逻辑IsSimilarType()来检查它并匹配任何通用参数。如果您需要使用T[],请使用T.MakeArrayType(1)。
Method<T>(T, T[])
IsGenericParameter == true
new Type()
public class T
T[]
T.MakeArrayType(1)