【问题标题】:C#: GetMethod by type (generic list)C#:按类型获取方法(通用列表)
【发布时间】:2021-12-14 23:29:23
【问题描述】:

我有一个类很少有泛型重载方法。我正在尝试通过其参数类型来获取特定的参数。当我坚持前两个(使用 int 和 string 类型的参数)时,它相对容易做到。但是无论我做什么,我都无法让我的程序注意到第三个,用于通用列表。我使用了错误的类型参数吗?如果是这样,什么是正确的方法?

/* rest of code */
    static void Main(string[] args) {
        MethodInfo method =
            typeof(c).GetMethod("m", new Type[] { typeof(int) });

        Console.WriteLine(method);

        method =
            typeof(c).GetMethod("m", new Type[] { typeof(String) });

        Console.WriteLine(method);

        method =
            typeof(c).GetMethod("m", new Type[] { typeof(IEnumerable<>) });

        Console.WriteLine(method);

        Console.ReadKey();
    }
}

static class c
{
    public static void m<T>(int i)
    {
    }

    public static void m<T>(String s)
    {
    }

    public static void m<T>(IEnumerable<T> Ls)
    {
    }
}

【问题讨论】:

标签: c# system.reflection


【解决方案1】:

短版:typeof(IEnumerable&lt;&gt;)typeof(IEnumerable&lt;T&gt;) 不同(对于某些T)。

更长的版本:没有方法void c.m(IEnumerable&lt;&gt; Ls),只有在泛型参数将是某些特定的情况下重载-存在于运行时-由于某些代码引用泛型的实例化而需要抖动来创建方法的类型方法。

在您的测试代码中添加对通用方法的某个实例的调用,然后为该实例执行GetMethod

考虑以下几点:

using System.Collections.Generic;
using System.Linq;
using static System.Console;

class Methods {
    public static void M(int x)     {
        // no-op
    }

    public static void M<T>(IEnumerable<T> x)     {
        // no-op
    }
}

class Program {
    static void Main(string[] args)  {
        Methods.M(0);
        Methods.M(new[] { "a", "b" });

        ShowAllM();
    }

    public static void ShowAllM() {
        var tm = typeof(Methods);
        foreach (var mi in tm.GetMethods().Where(m => m.Name == "M"))
        {
            WriteLine(mi.Name);
            foreach (var p in mi.GetParameters())
            {
                WriteLine($"\t{p.ParameterType.Name}");
            }
        }
    }
}

产生输出:

米 整数32 米 IEnumerable`1

请注意,泛型重载只有一个结果。如果对M&lt;char&gt;(…) 的调用添加到Main,则输出相同

对于反射只有一种方法,它的参数是否反映了它的“开放泛型”性质,但这与可以使用开放泛型类型(例如IEnumerable&lt;&gt;)调用的方法不同,因为开放类型不是可实例化。

(我在这里捏造了很多技术细节。看看调试器中typeof(IEnumerable&lt;&gt;)typeof(IEnumerable&lt;int&gt;) 之间的区别很有启发性。)

【讨论】:

  • 是否可以添加代码示例?我不认为我理解您所说的 GetMethod for that instance 是什么意思。
【解决方案2】:

第三种方法的签名为 m&lt;T&gt;(IEnumerable&lt;T&gt;),但您的示例显示了尝试查找签名为 m(IEnumerable&lt;&gt;) 的方法。

typeof(IEnumerable&lt;T&gt;)typeof(IEnumerable&lt;&gt;) 的区别在于前者是泛型类型,后者是泛型类型定义,它们不是一回事。泛型类型由泛型类型定义和泛型类型参数确定。

考虑到这一点,您会想要使用:

method =
        typeof(c).GetMethod("m", new Type[] { typeof(IEnumerable<MyType>) });

并替换您将传递给方法的可枚举类型。

另一方面,如果您事先不知道可枚举的类型,您可以获取泛型方法定义并在需要时创建可用的泛型方法:

methodDef =
        typeof(c).GetMethod("m", new Type[] { typeof(IEnumerable<object>) }).GetGenericMethodDefinition();
method = methodDef.MakeGenericMethod(new Type[] { typeof(MyType) });

【讨论】:

  • 是否有可能在不改变任何方法的情况下获得它?目前我正在使用外部库,所以更改并不是一个真正的选择。我知道我可以通过 GetMethods() 获取相关的 MethodInfo 并搜索到正确的,但它是如此粗略
  • @Daedan 是的,该方法很好,我的示例应该可以获取 MethodInfo ,您可以使用它来调用假设这是您想要做的。获得 MethodInfo 后,您是如何使用它的?
  • 我没有 method = typeof(c).GetMethod("m", new Type[] { typeof(IEnumerable&lt;object&gt;) }); 返回 null。要获得 MethodInfo,我必须修改我的方法以明确将 IEnumerable 作为参数
  • @Daedan 嗯,我无法自己测试它。尝试搜索基于 Linq 的方法,获取所有方法,然后过滤泛型:GetMethods().Where(m => m.IsGenericMethod...)。有关完整示例,请参阅 Max Dove 的答案。
【解决方案3】:

如果您从 int 和 string 方法中删除通用定义:

    public static void m(int i)
    {
    }

    public static void m(String s)
    {
    }

    public static void m<T>(IEnumerable<T> Ls)
    {
    }

并使用以下几行来获取所需的通用方法:

method = typeof(c).GetMethods().FirstOrDefault(m => m.IsGenericMethod &&
                    m.GetParameters()[0].ParameterType.GetGenericTypeDefinition()
                        == typeof(IEnumerable<>));

这样就可以了

【讨论】:

    【解决方案4】:
    /// <summary>
    /// Will fetch first occurence of IEnumerable<T> method and generate new generic method 
    /// <para/>
    /// that corresponds to Document type
    /// </summary>
    /// <param name="Document"></param>
    /// <param name="MethodName"></param>
    /// <returns></returns>
    public static MethodInfo GetAppropriateCollectionGenericMethod(object SourceClass, dynamic Document, string MethodName)
    {
    
        //get all public methods
        var publicMethods = SourceClass.GetType().GetMethods().Where(x => x.Name == MethodName && x.IsGenericMethod);
    
        //filter out only useful methods
        foreach (var goodMethod in publicMethods)
        {
            var methodParams = goodMethod.GetParameters();
            var firstParameterType = methodParams[0].ParameterType;
            //methods that has arguments like Ienumerable<T>, RepeatedField<T> and so on
            var hasNested = firstParameterType.GenericTypeArguments.Length > 0;
            if (hasNested == true)
            {
                //if we found first method with that name that has as parameter an IEnumerable<T> we are ok
                var genericTypeDef = firstParameterType.GetGenericTypeDefinition();
                if (genericTypeDef == typeof(IEnumerable<>))
                {
                    //Recover current document type, even if it's a list of such types
                    Type documentType = GetDocumentNestedType(Document);
                    //simply create a generic method based on Document inner Type
                    return goodMethod.MakeGenericMethod(documentType);
                }
            }
        }
    
        return null;
    
    }
    

    您将需要这个,以避免错误:

    var hasNested = firstParameterType.GenericTypeArguments.Length > 0;

    这将获取第一次出现的:

    public static void m<T>(IEnumerable<T> Ls)
    {
    }
    

    并将生成一个您可以使用的方法:

    var localMethod = GenericReflectionHelper.GetAppropriateCollectionGenericMethod(this, Document, nameof(Insert));
    
    //we are relying on implicit casting
    
    localMethod.Invoke(this, new object[] { Document });
    

    完整样本:

    public void Insert<T>(T Document)
            {
                //Valid for Lists and Repeated Fields
                if (Document is IEnumerable)
                {
    
                    MethodInfo localMethod;
                    var tuple = Tuple.Create(Document.GetType(), nameof(Insert));
                    if (CachedMethodsRedirection.ContainsKey(tuple) == true)
                    {
                        localMethod = CachedMethodsRedirection[tuple];
                    }
                    else
                    {
                        localMethod = GenericReflectionHelper.GetAppropriateCollectionGenericMethod(this, Document, nameof(Insert));
                        CachedMethodsRedirection.Add(tuple, localMethod);
                    }
    
                    //we are relying on implicit casting
                    localMethod.Invoke(this, new object[] { Document });
    
                }
                else
                {
    
                    DocumentSession.GetCollection<T>().Insert(Document);
    
                }
            }
    
    public void Insert<T>(IEnumerable<T> Document)
            {
                DocumentSession.GetCollection<T>().Insert(Document);
            }
    

    【讨论】:

    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 2023-03-17
    • 2020-04-11
    • 1970-01-01
    • 1970-01-01
    • 2010-10-08
    • 2019-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多