Lambda与BackgroundWorker

Kyl*_*leM 3 c# lambda

为什么以下语法有效?

BackgroundWorker bw = new BackgroundWorker();
String one = resultFromSomeMethod();
String two = resultFromOtherMethod();
String three = resultFromThirdMethod();
bw.DoWork += (a,b) => TestMethod(one, two, three);
Run Code Online (Sandbox Code Playgroud)

其中TestMethod定义为:

private void TestMethod(String one, String two, String three){
   //Do Stuff!!
}
Run Code Online (Sandbox Code Playgroud)

DoWorkEventHandler被定义为一个带有两个参数的委托:object sender和EventArgs e.但是,上面的TestMethod没有这样的参数.通过我对委托的理解,要创建一个新的委托,该方法必须符合委托的声明.我似乎通过使用lambda绕过了这个限制.上面的语法如何以及为什么工作,即使我尝试创建new DoWorkEventHandler(TestMethod)它肯定不会工作?

我阅读了Eric White 关于Lambda Expressions 的博客,但它似乎没有回答这个问题.

Han*_*ney 10

让我为你澄清一下lambda:

(a,b) => TestMethod("", "", "");
Run Code Online (Sandbox Code Playgroud)

翻译为:

private void AnonymousLambda(object a, EventArgs b)
{
    TestMethod("", "", "");
}
Run Code Online (Sandbox Code Playgroud)

lambda的语法是:

implicit method arguments => method body
Run Code Online (Sandbox Code Playgroud)

隐式方法参数由方法分配给(或调用)的预期签名确定.因为你这个分配lambda表达式的DoWorkEventHandler委托,就自动地解释a,并bobjectEventArgs分别.

在某些情况下,编译器无法推断lambda参数的隐式类型,因此您也可以明确地编写它们:

(object a, EventArgs b) => TestMethod("", "", "");
Run Code Online (Sandbox Code Playgroud)

所以你真的在你匿名创建的另一种方法体内调用你的TestMethod!而外部方法有DoWorkEventHandler方法签名.

编辑

根据有关字符串变量的其他问题详细信息,编译器将在代码上创建一个闭包,以将变量包含在方法范围内.代码基本上成为一个新类,它将变量保存为私有字段,然后在调用中引用它们:

public class AnonymousClosureClass
{
    public void AnonymousLambda(object a, EventArgs b)
    {
        // Note the reference to the original class instance
        String one = originalClassReference.one;
        String two = originalClassReference.two;
        String three = originalClassReference.three;
        TestMethod(one, two, three);
    }
}
Run Code Online (Sandbox Code Playgroud)

您的主要代码实际上变为:

BackgroundWorker bw = new BackgroundWorker();

// Note: these may become field members to remain visible to the closure class
String one = resultFromSomeMethod();
String two = resultFromOtherMethod();
String three = resultFromThirdMethod();

var closure = new AnonymousClosureClass();
bw.DoWork += closure.AnonymousLambda;
Run Code Online (Sandbox Code Playgroud)

最后,值得注意的是,这根本不是实际生成的闭包代码的命名或外观.

  • 您将`TestMethod`参数包装在与委托签名匹配的函数中,因此它可以正常工作.这也称为适配器模式,虽然是一个松散的例子. (3认同)
  • @DavidHaney你不想避免关闭.闭包是一个令人惊叹的*工具,它提供了很多功能,并且可以大大简化代码.您只需要意识到闭包关闭变量,而不是值,并相应地编码.仅仅因为这一点而避免这种惊人的功能是非常浪费的. (2认同)

Bil*_*ill 5

TestMethod正在从(A,B)函数调用.

这样看是这样的:

BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += DoWorkFunc;

void DoWorkFunc(object a, EventArgs b)
{
    TestMethod("", "", "");
}
Run Code Online (Sandbox Code Playgroud)


Tim*_* S. 5

参数在(a,b)代码部分中定义.这些对应于object senderDoWorkEventArgs e.也就是说,您的lambda类似于以下代码:

BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(MyFunction);


void MyFunction(object a, DoWorkEventArgs b)
{
    TestMethod("", "", "");
}
Run Code Online (Sandbox Code Playgroud)

您没有使用参数或返回值的事实TestMethod是无关紧要的.

(与lambda等价物相比)Func<int, int>myFunc = c => c + 1

int MyFunction(int c)
{
    return c + 1;
}
Run Code Online (Sandbox Code Playgroud)


Dmi*_*try 5

委托的论点根本就没有用过.Lambda等效于以下方法:

void LambdaMethod(object a, DoWorkEventArgs b)
{
    TestMethod("", "", "");
}
Run Code Online (Sandbox Code Playgroud)