C# 作为事件处理程序的lambda表达式如何更改局部变量?

C# 作为事件处理程序的lambda表达式如何更改局部变量?,c#,lambda,event-handling,C#,Lambda,Event Handling,我正在为我的一个类编写一些测试,我需要测试是否引发了一个事件。我只是尝试一下,看看发生了什么,然后编写了类似于以下极其简化的代码的代码 public class MyEventClass { public event EventHandler MyEvent; public void MethodThatRaisesMyEvent() { if (MyEvent != null) MyEvent(this, new EventArg

我正在为我的一个类编写一些测试,我需要测试是否引发了一个事件。我只是尝试一下,看看发生了什么,然后编写了类似于以下极其简化的代码的代码

public class MyEventClass
{
    public event EventHandler MyEvent;
    public void MethodThatRaisesMyEvent()
    {
        if (MyEvent != null)
            MyEvent(this, new EventArgs());
    }
}

[TestClass]
public class MyEventClassTest
{
    [TestMethod]
    public void EventRaised()
    {
        bool raised = false;
        var subject = new MyEventClass();
        subject.MyEvent += (s, e) => raised = true;

        subject.MethodThatRaisesMyEvent();

        Assert.IsTrue(raised);
    }
}
当我开始尝试弄清楚它是如何工作的时候,我并没有感到惊讶。具体来说,在没有lambda表达式的情况下,如何编写此代码,以便可以更新局部变量
raised
?换句话说,编译器是如何重构/翻译这一点的

我走了这么远

[TestClass]
public class MyEventClassTestRefactor
{
    private bool raised;

    [TestMethod]
    public void EventRaised()
    {
        raised = false;
        var subject = new MyEventClass();
        subject.MyEvent += MyEventHandler;

        subject.MethodThatRaisesMyEvent();

        Assert.IsTrue(raised);
    }

    private void MyEventHandler(object sender, EventArgs e)
    {
        raised = true
    }
}
但这将
提升为类作用域字段,而不是局部作用域变量

具体来说,在没有lambda表达式的情况下,如何编写此代码,以便更新所引发的局部变量

您将创建一个额外的类来保存捕获的变量。这就是C#编译器所做的。额外的类将包含一个带有lambda表达式主体的方法,
EventRaised
方法将使用该实例中的变量而不是“真实”的局部变量创建该捕获类的实例

在不使用事件的情况下演示这一点是最简单的——只是一个小型控制台应用程序。以下是使用lambda表达式的版本:

using System;

class Test
{
    static void Main()
    {
        int x = 10;
        Action increment = () => x++;

        increment();
        increment();
        Console.WriteLine(x); // 12
    }
}
下面的代码与编译器生成的代码类似:

using System;

class Test
{
    private class CapturingClass
    {
        public int x;

        public void Execute()
        {
            x++;
        }
    }

    static void Main()
    {
        CapturingClass capture = new CapturingClass();
        capture.x = 10;
        Action increment = capture.Execute;

        increment();
        increment();
        Console.WriteLine(capture.x); // 12
    }
}
当然,它可能会变得比这复杂得多,特别是如果您有多个具有不同作用域的捕获变量,但是如果您能够理解上面的工作原理,这是一个很大的第一步

具体来说,在没有lambda表达式的情况下,如何编写此代码,以便更新所引发的局部变量

您将创建一个额外的类来保存捕获的变量。这就是C#编译器所做的。额外的类将包含一个带有lambda表达式主体的方法,
EventRaised
方法将使用该实例中的变量而不是“真实”的局部变量创建该捕获类的实例

在不使用事件的情况下演示这一点是最简单的——只是一个小型控制台应用程序。以下是使用lambda表达式的版本:

using System;

class Test
{
    static void Main()
    {
        int x = 10;
        Action increment = () => x++;

        increment();
        increment();
        Console.WriteLine(x); // 12
    }
}
下面的代码与编译器生成的代码类似:

using System;

class Test
{
    private class CapturingClass
    {
        public int x;

        public void Execute()
        {
            x++;
        }
    }

    static void Main()
    {
        CapturingClass capture = new CapturingClass();
        capture.x = 10;
        Action increment = capture.Execute;

        increment();
        increment();
        Console.WriteLine(capture.x); // 12
    }
}

当然,它可能会变得比这复杂得多,特别是如果您有多个具有不同作用域的捕获变量,但如果您能够理解上述工作原理,这将是一个重要的第一步。

编译器生成这样的类,该类具有lambda delegate签名的方法。所有捕获的局部变量都移动到此类字段:

public sealed class c_0
{
    public bool raised;

    public void m_1(object s, EventArgs e)
    {
       // lambda body goes here
       raised = true;
    }
}
最后一个编译器技巧是用生成类的这个字段替换local
raised
变量的用法:

[TestClass]
public class MyEventClassTest
{
    [TestMethod]
    public void EventRaised()
    {
        c_0 generated = new c_0();
        generated.raised = false;

        var subject = new MyEventClass();
        subject.MyEvent += generated.m_1;

        subject.MethodThatRaisesMyEvent();

        Assert.IsTrue(generated.raised);
    }
}

编译器生成这样的类,该类具有签名为lambda委托的方法。所有捕获的局部变量都移动到此类字段:

public sealed class c_0
{
    public bool raised;

    public void m_1(object s, EventArgs e)
    {
       // lambda body goes here
       raised = true;
    }
}
最后一个编译器技巧是用生成类的这个字段替换local
raised
变量的用法:

[TestClass]
public class MyEventClassTest
{
    [TestMethod]
    public void EventRaised()
    {
        c_0 generated = new c_0();
        generated.raised = false;

        var subject = new MyEventClass();
        subject.MyEvent += generated.m_1;

        subject.MethodThatRaisesMyEvent();

        Assert.IsTrue(generated.raised);
    }
}

乔恩·斯凯特又来营救了!赞成!我希望你或利珀特先生能回答!接受(尽快)。乔恩·斯基特又来营救了!赞成!我希望你或利珀特先生能回答!接受(尽快)。带有俄罗斯口音!令人惊叹的!带着俄罗斯口音!令人惊叹的!