【问题标题】:Select Right Generic Method with Reflection使用反射选择正确的通用方法
【发布时间】:2010-09-02 21:45:49
【问题描述】:

我想通过反射选择正确的泛型方法,然后调用它。

通常这很容易。例如

var method = typeof(MyType).GetMethod("TheMethod");
var typedMethod = method.MakeGenericMethod(theTypeToInstantiate);

但是,当方法有不同的泛型重载时,问题就开始了。例如 System.Linq.Queryable 类中的静态方法。 “Where”方法有两种定义

static IQueryable<T> Where(this IQueryable<T> source, Expression<Func<T,bool>> predicate)
static IQueryable<T> Where(this IQueryable<T> source, Expression<Func<T,int,bool>> predicate)

这意味着 GetMethod 不起作用,因为它无法区分这两者。因此,我想选择正确的。

到目前为止,我经常只采用第一种或第二种方法,这取决于我的需要。像这样:

var method = typeof (Queryable).GetMethods().First(m => m.Name == "Where");
var typedMethod = method.MakeGenericMethod(theTypeToInstantiate);

但是我对此并不满意,因为我做了一个很大的假设,即第一种方法是正确的。我宁愿通过参数类型找到正确的方法。但我不知道怎么做。

我尝试过传递“类型”,但没有成功。

        var method = typeof (Queryable).GetMethod(
            "Where", BindingFlags.Static,
            null,
            new Type[] {typeof (IQueryable<T>), typeof (Expression<Func<T, bool>>)},
            null);

所以有人知道我如何通过反射找到“正确”的通用方法。例如 Queryable 类上的“Where”方法的正确版本?

【问题讨论】:

    标签: c# reflection


    【解决方案1】:

    您可以在编译时优雅地选择方法的特定泛型重载,而无需像此处的其他答案那样将任何字符串传递给运行时搜索。

    静态方法

    假设您有多个同名的静态方法,例如:

    public static void DoSomething<TModel>(TModel model)
    
    public static void DoSomething<TViewModel, TModel>(TViewModel viewModel, TModel model)
    
    // etc
    

    如果您创建的 Action 或 Func 与您要查找的重载的泛型计数和参数计数相匹配,您可以在编译时选择它,并使用相对较少的杂技。

    示例:选择第一种方法 - 返回 void,因此使用一个 Action,采用一个泛型。我们使用 object 来避免指定类型:

    var method = new Action<object>(MyClass.DoSomething<object>);
    

    示例:选择第二种方法 - 返回 void,因此 Action,2 个泛型类型,因此使用类型对象两次,2 个泛型参数各使用一次:

    var method = new Action<object, object>(MyClass.DoSomething<object, object>);
    

    您只需获得所需的方法,无需进行任何疯狂的管道操作,也无需运行时搜索或使用有风险的字符串。

    方法信息

    通常在反射中您需要 MethodInfo 对象,您也可以通过编译安全的方式获得该对象。这是当您传递要在方法中使用的实际泛型类型时。假设你想要上面的第二种方法:

    var methodInfo = method.Method.MakeGenericMethod(type1, type2);
    

    您的通用方法没有任何反射搜索或对 GetMethod() 的调用或脆弱的字符串。

    静态扩展方法

    您在 Queryable.Where 中引用的具体示例迫使您对 Func 定义有所了解,但通常遵循相同的模式。 The signature for the most commonly used Where() extension method 是:

    public static IQueryable<TModel> Where<TModel>(this IQueryable<TModel>, Expression<Func<TModel, bool>>)
    

    显然这会稍微复杂一些 - 这里是:

    var method = new Func<IQueryable<object>,
                          Expression<Func<object, bool>>,
                          IQueryable<object>>(Queryable.Where<object>);
    
    var methodInfo = method.Method.MakeGenericMethod(modelType);
    

    实例方法

    结合 Valerie 的评论 - 要获得实例方法,您需要做一些非常相似的事情。假设你的类中有这个实例方法:

    public void MyMethod<T1>(T1 thing)
    

    首先选择方法和静态方法一样:

    var method = new Action<object>(MyMethod<object>);
    

    然后调用 GetGenericMethodDefinition() 以获取通用 MethodInfo,最后使用 MakeGenericMethod() 传递您的类型:

    var methodInfo = method.Method.GetGenericMethodDefinition().MakeGenericMethod(type1);
    

    解耦 MethodInfo 和参数类型

    问题中没有要求这样做,但是一旦您执行上述操作,您可能会发现自己在一个地方选择方法,并决定在另一个地方传递什么类型。您可以将这两个步骤解耦。

    如果您不确定要传入的泛型类型参数,您始终可以在没有它们的情况下获取 MethodInfo 对象。

    静态:

    var methodInfo = method.Method;
    

    实例:

    var methodInfo = method.Method.GetGenericMethodDefinition();
    

    然后将其传递给其他知道要实例化类型的方法并调用该方法 - 例如:

    processCollection(methodInfo, type2);
    
    ...
    
    protected void processCollection(MethodInfo method, Type type2)
    {
        var type1 = typeof(MyDataClass);
        object output = method.MakeGenericMethod(type1, type2).Invoke(null, new object[] { collection });
    }
    

    这特别有帮助的一件事是从类内部选择类的特定实例方法,然后将其公开给以后需要各种类型的外部调用者。

    附录

    下面的一些 cmets 说他们无法让这个工作。我不必经常选择像这样的通用方法可能并不奇怪,但我今天碰巧这样做了,在幕后使用的经过良好测试的代码中,所以我想我会提供现实世界的例子 - 也许它会帮助那些努力让它发挥作用的人。

    C# 缺少 Clone 方法,所以我们有自己的方法。它可以采用许多参数,包括解释如何在源对象内递归复制 IEnumerable 属性的参数。

    复制 IEnumerable 的方法名为 CopyList,如下所示:

    public static IEnumerable<TTo> CopyList<TTo>(
        IEnumerable<object> from,
        Func<PropertyInfo, bool> whereProps,
        Dictionary<Type, Type> typeMap
    )
        where TTo : new()
    {
    

    为了使事情复杂化(并锻炼这种方法的肌肉),它有几个重载,比如这个:

    public static IEnumerable<TTo> CopyList<TTo>(
        IEnumerable<object> from,
        Dictionary<Type, Type> typeMap
    )
        where TTo : new()
    {
    

    所以,我们有几个(我只向您展示 2 个,但代码中还有更多)方法签名。它们具有相同数量的通用参数,但不同数量的方法参数。名称是相同的。我们怎么可能调用正确的方法?开始 C# 忍者!

    var listTo = ReflectionHelper.GetIEnumerableType(
        fromValue.GetType());
    
    var fn = new Func<
        IEnumerable<object>,
        Func<PropertyInfo, bool>,
        Dictionary<Type, Type>,
        IEnumerable<object>>(
            ModelTransform.CopyList<object>);
    
    var copyListMethod = fn.GetMethodInfo()
        .GetGenericMethodDefinition()
        .MakeGenericMethod(listTo);
    
    copyListMethod.Invoke(null,
        new object[] { fromValue, whereProps, typeMap });
    

    第一行使用了一个辅助方法,我们将在后面讨论,但它所做的只是在该属性中获取 IEnumerable 列表的泛型类型,并将其分配给listTo。下一行是我们真正开始执行此技巧的地方,我们在其中布置了具有足够参数的Func,以匹配我们打算获取的特定CopyList() 重载。具体来说,我们想要的 CopyList() 有 3 个参数,并返回 IEnumerable&lt;TTo&gt;。请记住,Func 将其返回类型作为其最后一个泛型 arg,并且我们将替换 object,只要我们打算获取的方法中有泛型。但是,正如您在此示例中所看到的,我们不需要在其他任何地方替换对象。例如,我们知道我们想要传递一个接受 PropertyInfo 并返回真/假 (bool) 的 where 子句,而我们只是在 Func 中直接说出这些类型。

    作为 Func 的构造函数参数,我们传递 CopyList() - 但请记住,名称 CopyList 由于方法重载而含糊不清。真正酷的是,C# 现在正在为您做艰苦的工作,通过查看 Func 参数并确定正确的参数。事实上,如果你弄错了 args 的类型或数量,Visual Studio 实际上会用错误标记该行:

    'CopyList' 没有重载匹配委托 'Func...'

    告诉你究竟需要修复什么还不够聪明,但如果你看到那个错误,你就很接近了 - 你需要仔细检查 args 和返回类型并将它们完全匹配,将 Generic args 替换为目的。

    在第三行,我们调用 C# 内置的 .GetMethodInfo(),然后是 .MakeGeneric(listTo)。我们只有一个 Generic 可以为此设置,因此我们将其传递为 listTo。如果我们有 2 个,我们会在这里传递 2 个参数。这些 Type 参数正在替换我们之前所做的 object 替换。

    就是这样——我们可以调用copyListMethod(),没有字符串,完全编译安全。最后一行进行调用,首先传递 null,因为它是一个静态方法,然后是一个带有 3 个参数的 object[] 数组。完成。

    我说过我会回到ReflectionHelper 方法。这里是:

    public static Type GetIEnumerableType(Type type)
    {
        var ienumerable = type.GetInterface(typeof(System.Collections.Generic.IEnumerable<>).FullName);
        var generics = ienumerable.GetGenericArguments();
        return generics[0];
    }
    

    【讨论】:

    • 我不知道为什么这不是最佳答案!它简单且类型安全。我让它为我们的代码工作,稍作改动。我正在调用一个非静态方法,并且必须先获取泛型定义,然后再将其转换为泛型方法:var methodInfo = method.Method.GetGenericMethodDefinition().MakeGenericMethod(type1);
    • 我同意瓦莱丽的观点。这就是我现在这样做的方式。不再有错误输入的字符串给我空引用异常...对于原始问题,需要 Func 而不是 Action 并且必须调用 GetGenericMethodDefinition()。
    • 我在调用 MakeGenericMethod 时收到了 InvalidOperationException,试图调用 Queryable.Where:System.Linq.IQueryable1[System.Object] Where[Object](System.Linq.IQueryable1[System.Object],System.Linq.Expressions。 Expression1[System.Func2[System.Object,System.Boolean]]) 不是 GenericMethodDefinition。 MakeGenericMethod 只能在 MethodBase.IsGenericMethodDefinition 为 true 的方法上调用。
    • 我一直用它在运行时获取特定的泛型方法,但我偶然发现了一个我无法解决的问题。我正在实现一个 EF 接口,该接口将一个内部类的实例传递给我,我需要获取一个在该实例上运行时键入的通用方法。它在所述接口方法内部工作正常,但我想提取获取通用方法信息的逻辑但无法提取,因为它是内部类型,我无法实例化它以访问该方法并将其放入Func。是否有解决方法,还是我需要传递实例?
    • @julealgon 这很复杂,值得提出自己的问题,因为答案可以帮助其他人,并且可能会从代码示例中受益。写下 Q 并链接到这个答案!
    【解决方案2】:

    可以,但是不好看!

    例如,要获得问题中提到的Where 的第一个重载,您可以这样做:

    var where1 = typeof(Queryable).GetMethods()
                     .Where(x => x.Name == "Where")
                     .Select(x => new { M = x, P = x.GetParameters() })
                     .Where(x => x.P.Length == 2
                                 && x.P[0].ParameterType.IsGenericType
                                 && x.P[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>)
                                 && x.P[1].ParameterType.IsGenericType
                                 && x.P[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>))
                     .Select(x => new { x.M, A = x.P[1].ParameterType.GetGenericArguments() })
                     .Where(x => x.A[0].IsGenericType
                                 && x.A[0].GetGenericTypeDefinition() == typeof(Func<,>))
                     .Select(x => new { x.M, A = x.A[0].GetGenericArguments() })
                     .Where(x => x.A[0].IsGenericParameter
                                 && x.A[1] == typeof(bool))
                     .Select(x => x.M)
                     .SingleOrDefault();
    

    或者如果你想要第二个重载:

    var where2 = typeof(Queryable).GetMethods()
                     .Where(x => x.Name == "Where")
                     .Select(x => new { M = x, P = x.GetParameters() })
                     .Where(x => x.P.Length == 2
                                 && x.P[0].ParameterType.IsGenericType
                                 && x.P[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>)
                                 && x.P[1].ParameterType.IsGenericType
                                 && x.P[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>))
                     .Select(x => new { x.M, A = x.P[1].ParameterType.GetGenericArguments() })
                     .Where(x => x.A[0].IsGenericType
                                 && x.A[0].GetGenericTypeDefinition() == typeof(Func<,,>))
                     .Select(x => new { x.M, A = x.A[0].GetGenericArguments() })
                     .Where(x => x.A[0].IsGenericParameter
                                 && x.A[1] == typeof(int)
                                 && x.A[2] == typeof(bool))
                     .Select(x => x.M)
                     .SingleOrDefault();
    

    【讨论】:

    • 如果你想调用得到的MethodInfo,你必须在它上面调用.MakeGenericMethod()并调用它返回MethodInfo
    • 如果你只有特定的参数,你将如何找到任何方法?如果不硬编码这 3 种类型(可查询、表达式、函数),它会是什么样子?
    【解决方案3】:

    这个问题大约有 2 年的历史,但我想出了(我认为是)一个优雅的解决方案,并认为我会与 StackOverflow 的优秀人员分享。希望它能帮助那些通过各种搜索查询到达这里的人。

    正如发帖者所说,问题在于获得正确的泛型方法。例如,一个 LINQ 扩展方法可能有大量的重载,类型参数嵌套在其他泛型类型中,都用作参数。我想做这样的事情:

    var where = typeof(Enumerable).GetMethod(
      "Where", 
      typeof(IQueryable<Refl.T1>), 
      typeof(Expression<Func<Refl.T1, bool>>
    );
    
    var group = typeof(Enumerable).GetMethod(
      "GroupBy", 
      typeof(IQueryable<Refl.T1>), 
      typeof(Expression<Func<Refl.T1, Refl.T2>>
    );
    

    如您所见,我创建了一些存根类型“T1”和“T2”,嵌套在“Refl”类(一个包含我所有的各种反射实用程序扩展函数等的静态类)中。它们用作类型参数通常去的地方的占位符。上面的例子分别对应于获取以下 LINQ 方法:

    Enumerable.Where(IQueryable<TSource> source, Func<TSource, bool> predicate);
    Enumerable.GroupBy(IQueryable<Source> source, Func<TSource, TKey> selector);
    

    所以很明显,Refl.T1 在这两个电话中都去了TSource 会去的地方; Refl.T2 代表 TKey 参数。TX 类声明如下:

    static class Refl {
      public sealed class T1 { }
      public sealed class T2 { }
      public sealed class T3 { }
      // ... more, if you so desire.
    }
    

    使用三个TX 类,您的代码可以识别包含最多三个泛型类型参数的方法。

    下一个魔术是实现通过GetMethods()进行搜索的函数:

    public static MethodInfo GetMethod(this Type t, string name, params Type[] parameters)
    {
        foreach (var method in t.GetMethods())
        {
            // easiest case: the name doesn't match!
            if (method.Name != name)
                continue;
            // set a flag here, which will eventually be false if the method isn't a match.
            var correct = true;
            if (method.IsGenericMethodDefinition)
            {
                // map the "private" Type objects which are the type parameters to
                // my public "Tx" classes...
                var d = new Dictionary<Type, Type>();
                var args = method.GetGenericArguments();
                if (args.Length >= 1)
                    d[typeof(T1)] = args[0];
                if (args.Length >= 2)
                    d[typeof(T2)] = args[1];
                if (args.Length >= 3)
                    d[typeof (T3)] = args[2];
                if (args.Length > 3)
                    throw new NotSupportedException("Too many type parameters.");
    
                var p = method.GetParameters();
                for (var i = 0; i < p.Length; i++)
                {
                    // Find the Refl.TX classes and replace them with the 
                    // actual type parameters.
                    var pt = Substitute(parameters[i], d);
                    // Then it's a simple equality check on two Type instances.
                    if (pt != p[i].ParameterType)
                    {
                        correct = false;
                        break;
                    }
                }
                if (correct)
                    return method;
            }
            else
            {
                var p = method.GetParameters();
                for (var i = 0; i < p.Length; i++)
                {
                    var pt = parameters[i];
                    if (pt != p[i].ParameterType)
                    {
                        correct = false;
                        break;
                    }
                }
                if (correct)
                    return method;
            }
        }
        return null;
    }
    

    上面的代码完成了大部分工作——它遍历特定​​类型的所有方法,并将它们与给定的参数类型进行比较以进行搜索。可是等等!那个“替代”功能呢?这是一个不错的小递归函数,它将搜索整个参数类型树——毕竟,参数类型本身可以是泛型类型,它可能包含 Refl.TX 类型,必须将其交换为“真实”类型参数对我们隐藏。

    private static Type Substitute(Type t, IDictionary<Type, Type> env )
    {
        // We only really do something if the type 
        // passed in is a (constructed) generic type.
        if (t.IsGenericType)
        {
            var targs = t.GetGenericArguments();
            for(int i = 0; i < targs.Length; i++)
                targs[i] = Substitute(targs[i], env); // recursive call
            t = t.GetGenericTypeDefinition();
            t = t.MakeGenericType(targs);
        }
        // see if the type is in the environment and sub if it is.
        return env.ContainsKey(t) ? env[t] : t;
    }
    

    【讨论】:

    • 很好的解决方案!我给这个答案一个 500 分的奖励来代替应得的认可。
    • @RexM 对于它的价值,我没有看到这在选择方法的特定泛型重载方面比我回答的方法更有效,而且它肯定是更多的代码。我发布的答案也不需要任何运行时递归。
    • @ChrisMoschini 用于比原始问题更复杂的情况,其中我们需要获取MethodInfo 并且当时只知道一些通用参数,这为消费者提供了一个很好的使用案例开发商。大量的代码很容易被单元测试封装和覆盖。
    • @RexM 你能举个例子吗?也许这属于一个单独的问题。这两种方法都可以在获取 MethodInfo 对象时只知道部分通用参数 - 我将更新我的答案来说明。我怀疑在编译时不确定方法的名称,这种方法性能较差,但没有提供额外的好处。
    • @ChrisMoschini 哦,我深表歉意!我的印象是你的答案是公认的:(
    【解决方案4】:

    您可能会发现另一个有用的解决方案 - 可以基于 Expression.Call 获得一个 MethodInfo,该Expression.Call 已经具有用于重载解析的逻辑。

    例如,如果您需要获取一些特定的Enumerable.Where 方法,可以使用以下代码完成:

    var mi = Expression.Call(typeof (Enumerable), "Where", new Type[] {typeof (int)},
                Expression.Default(typeof (IEnumerable<int>)), Expression.Default(typeof (Func<int, int, bool>))).Method;
    

    示例中的第三个参数 - 描述通用参数的类型,所有其他参数 - 参数的类型。

    以同样的方式,甚至可以获得非静态对象的泛型方法。您只需将第一个参数从typeof (YourClass) 更改为Expression.Default(typeof (YourClass))

    实际上,我在 plugin 中使用了这种方法来实现 .NET 反射 API。你可以检查它是如何工作的here

    【讨论】:

      【解决方案5】:

      让编译器为你做这件事:

      var fakeExp = (Expression<Func<IQueryable<int>, IQueryable<int>>>)(q => q.Where((x, idx) => x> 2));
      var mi = ((MethodCallExpression)fakeExp.Body).Method.GetGenericMethodDefinition();
      

      对于带索引的 Where ,或者简单地将 Where 表达式中的第二个参数省略为不带索引的那个

      【讨论】:

        【解决方案6】:

        除了@MBoros 的回答。

        您可以避免使用此辅助方法编写复杂的泛型参数:

        public static MethodInfo GetMethodByExpression<Tin, Tout>(Expression<Func<IQueryable<Tin>, IQueryable<Tout>>> expr)  
        {  
            return (expr.Body as MethodCallExpression).Method;  
        } 
        

        用法:

        var where = GetMethodByExpression<int, int>(q => q.Where((x, idx) => x > 2));  
        

        或者

        var select = GetMethodByExpression<Person, string>(q => q.Select(x => x.Name));  
        

        【讨论】:

          【解决方案7】:

          使用DynamicMethods.GenericMethodInvokerMethod、GetMethod is not enough 与泛型一起使用

          【讨论】:

            【解决方案8】:

            当您在编译时知道方法名称时,Chris Moschini 的回答很好。如果我们在运行时获取方法名称,Antamir 的答案就可以工作,但是这有点矫枉过正。

            我正在使用另一种方式,为此我使用来自 .NET 函数 Expression.Call 的反射器获得了灵感,该函数从字符串中选择正确的泛型方法。

            public static MethodInfo GetGenericMethod(Type declaringType, string methodName, Type[] typeArgs, params Type[] argTypes) {
                foreach (var m in from m in declaringType.GetMethods()
                                    where m.Name == methodName
                                        && typeArgs.Length == m.GetGenericArguments().Length
                                        && argTypes.Length == m.GetParameters().Length
                                    select m.MakeGenericMethod(typeArgs)) {
                    if (m.GetParameters().Select((p, i) => p.ParameterType == argTypes[i]).All(x => x == true))
                        return m;
                }
            
                return null;
            }
            

            用法:

            var m = ReflectionUtils.GetGenericMethod(typeof(Queryable), "Where", new[] { typeof(Person) }, typeof(IQueryable<Person>), typeof(Expression<Func<Person, bool>>));
            

            如果你只需要泛型方法定义,或者当时根本不知道T的类型,你可以使用一些伪造的类型,然后剥离泛型的信息:

            var m = ReflectionUtils.GetGenericMethod(typeof(Queryable), "Where", new[] { typeof(object) }, typeof(IQueryable<object>), typeof(Expression<Func<object, bool>>));
            m = m.GetGenericMethodDefinition();
            

            【讨论】:

              【解决方案9】:

              我做了一个小助手函数:

              Func<Type, string, Type[], Type[], MethodInfo> getMethod = (t, n, genargs, args) =>
                  {
                      var methods =
                          from m in t.GetMethods()
                          where m.Name == n && m.GetGenericArguments().Length == genargs.Length
                          let mg = m.IsGenericMethodDefinition ? m.MakeGenericMethod(genargs) : m
                          where mg.GetParameters().Select(p => p.ParameterType).SequenceEqual(args)
                          select mg
                          ;
              
                      return methods.Single();
                  };
              

              适用于简单的非泛型:

              var m_movenext = getMethod(typeof(IEnumerator), nameof(IEnumerator.MoveNext), Type.EmptyTypes, Type.EmptyTypes);
              

              喜欢复杂的泛型:

              var t_source = typeof(fillin1);
              var t_target = typeof(fillin2);
              var m_SelectMany = getMethod(
                         typeof(Enumerable), 
                         nameof(Enumerable.SelectMany), 
                         new[] { 
                             t_source, 
                             t_target 
                         }, 
                         new[] {
                             typeof(IEnumerable<>).MakeGenericType(t_source),
                             typeof(Func<,>).MakeGenericType(t_source, typeof(IEnumerable<>).MakeGenericType(t_target)) 
                         });
              

              【讨论】:

                【解决方案10】:

                我有一个类似的问题,我想我会在这里发布我的解决方案。我正在尝试调用几个函数:

                p.Foo<Klass1>(true)
                p.Foo<Klass2>(true)
                p.Foo<Klass3>(true)
                bool k1 = p.Bar<Klass1>()
                bool k2 = p.Bar<Klass2>()
                bool k3 = p.Bar<Klass3>()
                

                我的解决方案:

                public static TAction RemapGenericMember<TAction>(object parent, Type target, TAction func) where TAction : Delegate { 
                    var genericMethod = func?.Method?.GetGenericMethodDefinition()?.MakeGenericMethod(target);
                    if (genericMethod.IsNull()) {
                        throw new Exception($"Failed to build generic call for '{func.Method.Name}' with generic type '{target.Name}' for parent '{parent.GetType()}'");
                    }
                    return (TAction)genericMethod.CreateDelegate(typeof(TAction), parent);
                }
                

                现在我可以打电话了:

                foreach(var type in supportedTypes) {
                   InvokeGenericMember<Action<bool>>(p, type, Foo<object>)(true);
                   bool x = InvokeGenericMember<Function<bool>>(p, type, Bar<object>)();
                }
                

                【讨论】:

                  【解决方案11】:

                  Antamir 的回答对我来说非常有用,但它有一个错误,即当您提供通用类型和具体类型的混合时,它不会验证找到的方法上的参数数量是否与传入的类型数量相匹配。

                  例如,如果您运行:

                  type.GetMethod("MyMethod",typeof(Refl.T1),typeof(bool))
                  

                  它无法区分两种方法:

                  MyMethod<T>(T arg1)
                  MyMethod<T>(T arg1, bool arg2)
                  

                  两个调用:

                  var p = method.GetParameters();   
                  

                  应该改为:

                  var p = method.GetParameters();   
                  if (p.Length != parameters.Length)
                  {
                      correct = false;
                      continue;
                  }
                  

                  另外,现有的两条“break”行都应该是“continue”。

                  【讨论】:

                    【解决方案12】:

                    在使用反射调用方法时,我发现了使用 iQuerable 表达式的最简单方法。请看下面的代码:

                    您可以根据需要使用 IQuerable 表达式。

                    var attributeName = "CarName";
                    var attributeValue = "Honda Accord";
                    
                    carList.FirstOrDefault(e => e.GetType().GetProperty(attributeName).GetValue(e, null) as string== attributeValue);
                    

                    【讨论】:

                      【解决方案13】:
                      var firstGenericParam = Type.MakeGenericMethodParameter(0);
                      var firstParam = typeof(IQueryable<>).MakeGenericType(firstGenericParam);
                      var funcType = typeof(Func<,>).MakeGenericType(firstGenericParam, typeof(bool));
                      //var funcType = typeof(Func<,,>).MakeGenericType(firstGenericParam, typeof(int), typeof(bool)); //for second version 
                      var secondParam = typeof(Expression<>).MakeGenericType(funcType);
                      var method = typeof(Queryable).GetMethod(nameof(Queryable.Where), new Type[] { firstParam, secondParam });
                      

                      【讨论】:

                      • 虽然您的回答可能会解决问题,但 including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。您可以编辑您的答案以添加解释并指出适用的限制和假设。 - From Review
                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多