GetMethod的泛型方法 [英] GetMethod for generic method

查看:879
本文介绍了GetMethod的泛型方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想检索的MethodInfo对于在可枚举类型的方法:

I'm trying to retrieve MethodInfo for Where method of Enumerable type:

typeof (Enumerable).GetMethod("Where", new Type[] { 
     typeof(IEnumerable<>), 
     typeof(Func<,>) 
})

但得到空。我究竟做错了什么?

but get null. What am I doing wrong?

推荐答案

这previous答案适用于某些情况下,但是:

That previous answer works for some cases, however:

  • 在它不处理嵌套的泛型类型,如的参数类型动作&LT; IEnumerable的&LT; T&GT;&GT; 。它将把所有的动作&LT;&GT; 的匹配,例如, string.Concat(IEnumerable的&LT;字符串&GT;) string.Concat&LT; T&GT;(IEnumerable的&LT; T&GT;)将如果搜索Concat的都匹配类型的IEnumerable&LT;&GT; 的字符串类型。什么是真正的理想是处理嵌套泛型类型的递归,而对待所有通用参数相互匹配,无论名称虽然不匹配的具体类型。
  • 在它返回匹配而不是抛出一个异常,如果结果是模糊的,如 type.GetMethod第一种方法()一样。所以,你可能会得到你想要的,如果你是幸运的方法,也可能没有。
  • 在某些时候,有必要指定的BindingFlags 为了避免歧义,比如当一个派生类的方法'隐藏'的基类方法。通常你想找到基类的方法,而不是在你知道你正在寻找的方法是在派生类中一个专门的案件。或者,你可以知道你正在寻找一个静态VS实例方法,公共VS私人等,不想匹配,如果它是不准确。
  • 在它没有解决另一个重大过错与 type.GetMethods(),因为它也不会找上一个方法时,搜索基接口方法接口类型。好吧,也许这就是被挑剔,但它在另外的getMethods重大缺陷()这一直是我的问题。
  • 电话 type.GetMethods()是低效的, type.GetMember(姓名,MemberTypes.Method,...)将返回唯一方法具有匹配的名称,而不是类型的所有方法。
  • 作为最后一个挑剔的名称 GetGenericMethod()可能会产生误导,因为你可能会试图找到一个,恰好有一个类型的非泛型方法参数某处的参数类型,由于泛型声明类型。
  • It doesn't handle nested generic types, such as a parameter type of Action<IEnumerable<T>>. It will treat all Action<> as matches, for example, string.Concat(IEnumerable<string>) and string.Concat<T>(IEnumerable<T>) will both match if searching for "Concat" with type IEnumerable<> on the string type. What is really desirable is handling nested generic types recursively, while treating all generic parameters as matching each other regardless of name while NOT matching concrete types.
  • It returns the first method matched rather than throwing an exception if the result is ambiguous, like type.GetMethod() does. So, you might get the method you wanted if you're lucky, or you might not.
  • Sometimes it will be necessary to specify BindingFlags in order to avoid ambiguity, such as when a derived class method 'hides' a base class method. You normally want to find base class methods, but not in a specialized case where you know the method you're looking for is in the derived class. Or, you might know you're looking for a static vs instance method, public vs private, etc. and don't want to match if it's not exact.
  • It doesn't address another major fault with type.GetMethods(), in that it also doesn't search base interfaces for methods when looking for a method on an interface type. OK, maybe that's being picky, but it's another major flaw in GetMethods() that has been a problem for me.
  • Calling type.GetMethods() is inefficient, type.GetMember(name, MemberTypes.Method, ...) will return only methods with a matching name instead of ALL methods in the type.
  • As a final nit-pick, the name GetGenericMethod() could be misleading, since you might be trying to find a non-generic method that happens to have a type parameter somewhere in a parameter type due to a generic declaring type.

下面是一个能够解决所有这些事情,并可以作为通用替换有缺陷的 GetMethod的版本()。需要注意的是设置两个扩展方法,其中一个的BindingFlags和一个没有(为方便起见)。

Here's a version that addresses all of those things, and can be used as a general-purpose replacement for the flawed GetMethod(). Note that two extension methods are provided, one with BindingFlags and one without (for convenience).

/// <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(类型)扩展方法,可以公开,可能是有用的自己。我知道,这个名字是不是很大 - 欢迎您拿出一个更好的,但它可能会很长,解释它做什么。另外,我补充另一个改进通过检查'裁判'和数组类型(裁判被忽略的匹配,但阵列的尺寸必须匹配)。

Note that the IsSimilarType(Type) extension method can be made public and might be useful on its own. I know, the name isn't great - you're welcome to come up with a better one, but it might get really long to explain what it does. Also, I added yet another improvement by checking for 'ref' and array types (refs are ignored for matching, but arrays dimensions must match).

所以,这是微软如何都做到了。这真的不是那么难。

So, that's how Microsoft should have done it. It's really not that hard.

是啊,我知道,你可以缩短一些使用LINQ的逻辑,但我不是的LINQ在这样低级别的例程一个巨大的风扇,并且也没有,除非LINQ的大约是容易遵循的原来的code,这往往并非如此,海事组织。

Yeah, I know, you can shorten some of that logic using Linq, but I'm not a huge fan of Linq in low-level routines like this, and also not unless the Linq is about as easy to follow as the original code, which is often NOT the case, IMO.

如果你爱LINQ的,你必须,你可以替换的最里面的部分IsSimilarType()本(原来8号线成1):

If you love Linq, and you must, you can replace the inner-most part of IsSimilarType() with this (turns 8 lines into 1):

if (thisArguments.Length == arguments.Length)
    return !thisArguments.Where((t, i) => !t.IsSimilarType(arguments[i])).Any();

最后一件事:如果你正在寻找一个通用的方法与通用参数,如方法&LT; T&GT;(T,T []),你'将不得不找到一个类型,它是一个通用的参数( IsGenericParameter ==真)来传递,因为通配符匹配中的参数类型(任何人都行, )。但是,你不能只是做新类型() - 你必须要找到一个真正的(或TypeBuilder建立一个)。为了更方便,我加了公共T类的声明,并添加逻辑 IsSimilarType()来检查它并匹配任何通用的参数。如果你需要一个 T [] ,只需使用 T.MakeArrayType(1)

One last thing: If you're looking for a generic method with a generic parameter, such as Method<T>(T, T[]), you'll have to find a Type which is a generic parameter (IsGenericParameter == true) to pass in for the parameter type (any one will do, because of the 'wildcard' matching). However, you can't just do new Type() - you have to find a real one (or build one with TypeBuilder). To make this easier, I added the public class T declaration, and added logic to IsSimilarType() to check for it and match any generic parameter. If you need a T[], just use T.MakeArrayType(1).

这篇关于GetMethod的泛型方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆