我正在研究使用lamba表达式来允许以强类型方式连接事件,但中间有一个监听器,例如给定以下类
class Producer
{
public event EventHandler MyEvent;
}
class Consumer
{
public void MyHandler(object sender, EventArgs e) { /* ... */ }
}
class Listener
{
public static void WireUp<TProducer, TConsumer>(
Expression<Action<TProducer, TConsumer>> expr) { /* ... */ }
}
一个事件将被连接为:
Listener.WireUp<Producer, Consumer>((p, c) => p.MyEvent += c.MyHandler);
但是,这会产生编译器错误:
CS0832:表达式树可能不包含赋值运算符
现在起初这似乎是合理的,尤其是在阅读了关于为什么表达式树不能包含赋值的解释之后。然而,尽管有 C# 语法,但+=
不是赋值,它是对Producer::add_MyEvent
方法的调用,正如我们可以从生成的 CIL 中看到的,如果我们只是正常连接事件:
L_0001: newobj instance void LambdaEvents.Producer::.ctor()
L_0007: newobj instance void LambdaEvents.Consumer::.ctor()
L_000f: ldftn instance void LambdaEvents.Consumer::MyHandler(object, class [mscorlib]System.EventArgs)
L_0015: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
L_001a: callvirt instance void LambdaEvents.Producer::add_MyEvent(class [mscorlib]System.EventHandler)
所以在我看来这是一个编译器错误,因为它抱怨不允许分配,但没有发生分配,只是一个方法调用。还是我错过了什么……?
编辑:
请注意,问题是“这种行为是编译器错误吗?”。抱歉,如果我不清楚我在问什么。
编辑 2
在阅读了 Inferis 的答案后,他说“此时 += 被认为是赋值”,这确实有些道理,因为此时编译器可能不知道它将被转换为 CIL。
但是,我不允许编写显式方法调用表单:
Listener.WireUp<Producer, Consumer>(
(p, c) => p.add_MyEvent(new EventHandler(c.MyHandler)));
给出:
CS0571:“Producer.MyEvent.add”:无法显式调用运算符或访问器
所以,我想问题归结为+=
在 C# 事件的上下文中的实际含义。它的意思是“为此事件调用 add 方法”还是“以尚未定义的方式添加到该事件”。如果是前者,那么这在我看来是一个编译器错误,而如果是后者,那么它有点不直观,但可以说不是一个错误。想法?