我想使用lambda表达式以强类型方式连接事件,并在中间加入监听器,例如给定以下类
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)
所以在我看来,这似乎是编译器的一个错误,因为它抱怨不允许赋值,但实际上并没有赋值,只是一个方法调用。或者我有什么遗漏吗...?编辑:请注意,问题是“这种行为是编译器的bug吗?”如果我没有清楚地表达我的问题,对不起。
编辑2:阅读Inferis的答案后,他说“在那个点上,+= 被认为是赋值”,这有些合理,因为此时编译器可以认为它将转化为CIL。但是我不被允许使用显式的方法调用形式:
Listener.WireUp<Producer, Consumer>(
(p, c) => p.add_MyEvent(new EventHandler(c.MyHandler)));
给出:
CS0571: 'Producer.MyEvent.add':不能显式调用运算符或访问器
所以,我想问题归结为在C#事件的上下文中,+=
实际上意味着什么。它是指“调用此事件的add方法”还是指“以尚未定义的方式添加到此事件”。如果是前者,则对我来说这似乎是编译器错误,而如果是后者,则可能不太直观,但可以说不是错误。思路?