是否可以将任意方法组作为参数传递给方法?

我想写一个如下的函数

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

后来,我可以用任何方法组调用MyFunction 。 像这样的东西。

 MyFunction(Object.Equals) 

如果我承诺签名,那么事情就可以了。

 public void MyFunction(Func f) { // do something with known delegate } ... MyFunction(Object.Equals) 

方法组Object.Equals很高兴被强制转换为已知的委托类型Func ,但我不想提交特定的签名。 我想将任何方法组传递给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.Equals) 

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

你可以传递一个代表:

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

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

 MyFunction(new Func(Object.Equals)); 

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

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

请记住, 方法组只是一组方法的名称 – 可能有一个或多个 – 它们可能是重载或扩展方法。 方法组不是在编译代码中保留的概念 – 编译器支持从方法组到委托的转换 – 但为了使这成为可能,编译器必须能够毫不含糊地解决您想要传递的方法。

如果函数明确定义方法的签名,则方法组仅作为函数的参数有效。 因此,例如: public void MyFunction( Func f )可以传递给一个方法组。

你最接近的是写一个接受代表类型的函数:

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

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

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

我想你可以使用这样的签名:

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

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

我按照他最初的预期定义了MyFunction,但重要的是要注意,无论这样做只适用于一个方法签名。 如果MethodInfo提供了有关函数的所有内容,则实际上可能不需要调用它。

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

然后我们将提供一个包装器,它接受一个methodName,type和一个调用对象。 调用对象即使是实例方法也不需要生成委托,但实际执行委托可能会失败,如果它是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。

通用方法

此外,上面的代码无法与通用方法一起正常工作。 以下是应用方法的generics类型的示例。

  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参数中定义的generics类型的情况下传入类,则上述方法可能不完全支持generics类中的方法。

输出/参考参数

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