我正在为我的一个类(class)编写一些测试,我需要测试是否引发了一个事件。通过尝试并查看发生了什么,我编写了类似于以下极其简化的代码的代码。
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
}
}
但这会将 raised
更改为类范围的字段而不是局部范围的变量。
最佳答案
Specifically, how would I write this without lambda expressions so that the local variable raised can be updated?
您将创建一个额外的类来保存捕获的变量。这就是 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
}
}
当然,它可能会变得远比这更复杂,特别是当您有多个不同范围的捕获变量时 - 但如果您能理解上述工作原理,那就是重要的第一步。
关于c# - 作为事件处理程序的 lambda 表达式如何更改局部变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13635420/