【问题标题】:Is it possible to pass an arbitrary method group as a parameter to a method?是否可以将任意方法组作为参数传递给方法?
【发布时间】:2018-08-23 03:45:21
【问题描述】:

我想写一个类似下面的函数

// The type 'MethodGroup' below doesn't exist.  This is fantasy-code.
public void MyFunction(MethodGroup g)
{
    // do something with the method group
}

后者,我可以用 any 方法组调用MyFunction。像这样。

MyFunction(Object.Equals)

如果我提交签名,那么一切正常。

public void MyFunction(Func<object, object, bool> f)
{
    // do something with known delegate
}
...
MyFunction(Object.Equals)

方法组Object.Equals 很高兴被强制转换为已知的委托类型Func&lt;object, object, bool&gt;,但我不想提交特定的签名。我想将任何方法组传递给MyFunction

方法组无法转换为System.Object

public void MyFunction(object o)
{
    // do something with o
}
...
MyFunction(Object.Equals) // doesn't work

我认为每个人都忘记了方法调用的大括号,并在某个时候发现了这一点。我希望这并不意味着方法组不是(或不能转换)为一流的对象。

我认为 Linq 表达式不会提供我正在寻找的那种通用性,但我肯定会遗漏一些东西。

我还应该提到,如果方法组包含重载,那也没问题,只要我有一种检查方法组的方法。

我将如何处理方法组? 我可以打印组中所有方法的所有签名(重载、扩展方法等),或者我可以“调用”带有一些参数的组(如果可能,让它解决组中的正确重载)。还有其他方法可以做这些事情,但它们是您可能想要使用方法组做的一些事情。

正如一些人所提到的,我可以接受Delegate,并在我调用MyFunction 时转换为特定的已知委托类型。

public void MyFunction(Delegate d)
{
    // do something with d
}
...
MyFunction((Func<object, object, bool>)Object.Equals)

但这与传递整个方法组并不完全相同。这会从组中选择一种方法并将其转换为特定的委托。我真的很想一口气通过整个小组。

【问题讨论】:

    标签: c#


    【解决方案1】:

    我认为这里的一个重要问题是:如果您可以通过一个方法组,您会怎么做?

    请记住,方法组只是一组方法的名称 - 可能有一个或多个 - 它们可能是重载或扩展方法。方法组不是保留在已编译代码中的概念 - 编译器支持从方法组到委托的转换 - 但为了实现这一点编译器必须能够准确无误地解析您打算使用的方法通过。

    只有当函数明确定义了方法的签名时,方法组才能作为函数的参数有效。因此,例如:public void MyFunction( Func&lt;int&gt; f ) 可以传递一个方法组。

    最接近的方法是编写一个接受类型 Delegate 的函数:

    public void MyFunction( Delegate d ) { ... }
    

    但您仍然无法传递方法组,因为没有从方法组到委托的转换。您必须强制转换为特定的代表签名:

    // call MyFunction with a particular delegate
    MyFunction( (Func<string>)myObj.ToString );  // method group conversion may occur
    

    【讨论】:

    • 为了回答你的问题,我会得到它的名字并记录下来。方法组中的每个方法都具有相同的名称和相同的目标对象。有很多用途
    • what would you do with a method group if you could ever be passed one? 在我的情况下(网络),我想将方法​​作为参数传递并获取其名称,以便在接收方找到相应的方法。目前在任何地方我都在使用nameof 并将其作为字符串传递,但如果我可以直接传递方法本身并仅在方法中获取名称会更酷
    【解决方案2】:

    你可以传递一个委托:

    public void MyFunction(Delegate d)
    {
        // do something with the method group
    }
    

    但是,您必须在调用方法时指定委托类型:

    MyFunction(new Func<object, object, bool>(Object.Equals));
    

    注意:上面的代码不是传递一个方法组,而是一个方法......我认为不可能传递一个方法组。你为什么要这么做?

    【讨论】:

      【解决方案3】:

      提问者想要这样做是因为他想要关于方法组的元数据对于组中的任何方法(例如名称)都是相同的,或者他想要对组中的所有方法执行某些操作。我相信nameof (C# Reference) 在这里最有用。

      我按照他最初的预期定义了 MyFunction,但重要的是要注意,这仅适用于 one 方法签名。如果 MethodInfo 提供了您需要的有关函数的所有信息,则实际上可能不需要调用它。

      public void MyFunction(Delegate d)
      {
          // do something with d
      }
      

      然后我们将提供一个包装器,它接受方法名、类型和可选的调用对象。调用对象不需要生成委托,即使它是实例方法,但如果它为 null,则实际执行委托可能会失败。为静态方法提供调用对象将导致绑定错误,即使创建委托也是如此。我编写了下面的方法来返回静态方法和实例方法,并将任何实例方法绑定到调用者,而静态方法绑定到 null。当调用者是实例时,您可以修改它以跳过静态方法,反之亦然。

      public void MyFunctionMethodGroup(string methodName, Type type, object caller)
      {
          var methods = type.GetMethods().Where(x => x.Name == methodName);
          foreach(var method in methods)
          {
              Delegate myDelegate = method.CreateDelegate(Expression.GetDelegateType(
                  method.GetParameters().Select(x => x.ParameterType)
                  .Concat(new[] { method.ReturnType })
                  .ToArray()), method.IsStatic ? null : caller);
              MyFunction(myDelegate);
          }
      }
      

      这里有几个调用示例。

      静态方法

      MyFunctionMethodGroup(nameof(String.IsNullOrEmpty), typeof(String), null);
      

      实例方法

      请注意,即使它是实例函数,您也可以从类名中调用该函数。如果您不需要调用该方法并且没有调用者,这会很好。您也可以从实例中调用它,而无需显式编写类名。

      string xyz = "2";
      MyFunctionMethodGroup(nameof(String.Contains), typeof(String), xyz);
      MyFunctionMethodGroup(nameof(xyz.Contains), xyz.GetType(), xyz);
      

      高级案例

      扩展方法

      值得记住的是,扩展方法在定义它们的命名空间中被称为静态方法。如果您需要实例参数来执行您想要在 MyFunction 中执行的操作,则必须将其作为与调用者分开的新参数传入,因为扩展方法是静态的,因此该调用者将为 null。

      通用方法

      此外,上面的代码不适用于泛型方法。下面是应用方法的泛型类型的示例。

         public void MyFunctionMethodGroup(string methodName, Type type, object caller = null, params Type[] genericParameters)
          {
              //GetMethods only returns public methods. If you need protected/private, modify as appropriate.
              var methods = type.GetMethods().Where(x => x.Name == methodName);
              foreach (var method1 in methods)
              {
                  MethodInfo method = method1.IsGenericMethod ? method1.MakeGenericMethod(genericParameters) : method1;
                  Type delegateType = Expression.GetDelegateType(
                      method.GetParameters().Select(x => x.ParameterType)
                      .Concat(new[] { method.ReturnType })
                      .ToArray());
                  Delegate myDelegate = method.CreateDelegate(delegateType, method.IsStatic ? null : caller);
                  MyFunction(myDelegate);
              }
          }
      

      泛型类

      如果传入的类没有Type参数中定义的泛型类型,上述方法可能不完全支持泛型类中的方法。

      输出/参考参数

      编写的代码不支持输出或引用参数。它也可能不支持 ref 参数。

      【讨论】:

        【解决方案4】:

        我认为你可以这样使用签名:

        MyFunc<T>(EventHandler<T> newDelegate)
        {
            object storedDelegate = newDelegate;
            // after that you can use storedDelegate later
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-11-30
          • 1970-01-01
          • 1970-01-01
          • 2020-03-16
          • 2015-02-12
          • 2010-10-05
          • 2023-03-09
          • 2013-04-02
          相关资源
          最近更新 更多