描述
以下代码示例有效。有一个方法调用,如下所示:
ProcessMethod(MethodA, 1);
(选项1)
问题
a)有没有办法像这样称呼它?
ProcessMethod(MethodA(1));
(选项2)
换句话说,有没有办法调用一个方法来接受类型为Action<T>
的委托,方法是传递包含参数的委托,如选项2所示,而不是传递参数分别如选项1所示?
b)如果是这样,代码会是什么样的?
c)每个选项的优缺点是什么(提供选项2)?
示例
using System;
namespace DelegateDemo
{
class Program
{
static void Main(string[] args)
{
new Demo().Run();
}
}
public class Demo
{
public void Run()
{
ProcessMethod(MethodA, 1); // Can this be invoked like this ProcessMethod(MethodA(1))?
}
private void ProcessMethod(Action<int> method, int i)
{
method(i);
}
private void MethodA(int i)
{
Console.WriteLine("MethodA: {0}", i);
}
}
}
答案 0 :(得分:2)
当您拥有一个包含调用方法所需的所有信息的单个对象(包括调用站点和参数)时,您尝试执行的操作的术语是闭包。
要在C#中创建闭包,您需要匿名委托,这些日子意味着lambda表达式。给定一个命名的方法,如MethodA
,您不能将带有参数的方法的“调用”直接传递给另一个方法,如下所示:
public void Run(Action<int> method) { }
public void MethodA(int x ) { }
// Does *not* work
Run(MethodA(5));
因为MethodA(5)
不再是方法,所以它是一个类型为void
的表达式,这是错误的。但是,您可以将此方法调用封装到类型为Action<int>
的 的闭包中:
Run(x => MethodA(5));
这准确地说明了您的要求:您将Action<int>
传递给Run
方法,并将参数困在呼叫站点。但是,这可能不你真正想要的是:如果你将Action<int>
传递给Run
方法,那么该方法将假设它需要将参数传递给方法调用本身,但你的lambda将忽略它。相反,您可以像@SLaks建议的那样,并完全删除参数:
public void Run(Action method) { }
public void MethodA(int x ) { }
Run(() => MethodA(5));
在这种情况下,没有参数进入lambda表达式,因此生成的委托将是Action
类型;你最终调用一个带参数的方法的事实隐藏在闭包内。
这是一种相当流行的技术,显然有一些好处:
var x = 6; Run(() => MethodA(x))
,即使变量超出范围,变量x
仍可用于关闭。Run(() => { MethodA(5); MethodB(5) });
然而,有一个权衡,主要是你对lambda的控制程度。例如,在您的情况下,您使用Run
方法,该方法最初使用Action<int>
和int
,并使用另一个方法调用一个方法。这可能意味着你的Run
方法假设,在某些时候,你传递给它的方法将使用某个整数值(在数据库中查找,用它计算一些结果等)。< / p>
如果您切换到闭包,现在允许调用者在传入的委托中按字面意义执行任何操作,例如:
Run(() => Console.WriteLine("hahaha! No int here!"));
在你的情况下,这可能完全没问题;你可能不太关心代表内部发生了什么。但是,如果你的逻辑假设传递的方法有特殊之处,那么使用匿名代理很容易绕过这些期望。
答案 1 :(得分:1)
您可以传递一个lambda表达式来捕获闭包中的参数:
public void Run()
{
ProcessMethod(() => MethodA(1));
}
private void ProcessMethod(Action method)
{
method();
}