但是,先前的答案在某些情况下适用:
这是解决所有这些问题的版本,可以用作有缺陷的的通用替代品getmethod()
。请注意,提供了两种扩展方法,一种带有BindingFlags,另一种没有(为了方便)。
/// <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”和数组类型(引用被忽略以进行匹配,但数组尺寸必须匹配)来添加了另一个改进。
因此,这就是Microsoft 这样做的方式。其实并不难。
是的,我知道,您可以使用Linq缩短某些逻辑,但是我不是像这样的底层例程对Linq的忠实拥护者,并且除非Linq与原始代码一样容易遵循,否则我不会这样做。 IMO常常不是这样。
如果您喜欢Linq,并且必须这样做,则可以用此替换最里面的部分IsSimilarType()
(将8行变成1):
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)
。