问题 关于事件的价值/参考类型的问题


在MSDN上,我发现以下内容:

public event EventHandler<MyEventArgs> SampleEvent;

public void DemoEvent(string val)
{
// Copy to a temporary variable to be thread-safe.
    EventHandler<MyEventArgs> temp = SampleEvent; 

是参考吗?
如果是这样,我不理解其含义,因为当SampleEvent变为null时,temp也是如此

    if (temp != null)
        temp(this, new MyEventArgs(val));
}

1731
2018-04-08 07:16


起源



答案:


这是与线程有关的偏执狂。如果 另一个 thread取消订阅最后一个处理程序 刚过 你已经检查过了 null, 它可以 成为  null 你会引发异常由于委托是不可变的,因此将委托的快照捕获到变量中会阻止这种情况发生。

当然,它确实有 其他 副作用,你可以(而不是)最终提高事件对一个认为已经取消订阅的对象...

但要强调 - 当多个线程订阅/取消订阅对象时,这只是一个问题,这是:罕见,而b:不完全合乎需要。


12
2018-04-08 07:20



一个很好但又精确的解释:o) - Neil Knight
请注意,你可以 总是 最终会针对认为已取消订阅的处理程序引发事件 - 因为它可能在您开始执行委托之后但在您到达取消订阅之前取消订阅。 - Jon Skeet
所以这里的关键事实(针对提问者的合理问题,即委托是引用类型)是“委托是不可变的”,所以这个任务“捕获[es] a 快照 代表“。对吧? - AakashM
我认为不可改变意味着它不会改变自己。不明白为什么把它分配给变量的原因。 - Petr
@Petr - 你抓住了 当前 委托字段的值。当代码订阅/取消订阅时,它 取代 这有一个 不同 参考(可能是 null)。 - Marc Gravell♦


答案:


这是与线程有关的偏执狂。如果 另一个 thread取消订阅最后一个处理程序 刚过 你已经检查过了 null, 它可以 成为  null 你会引发异常由于委托是不可变的,因此将委托的快照捕获到变量中会阻止这种情况发生。

当然,它确实有 其他 副作用,你可以(而不是)最终提高事件对一个认为已经取消订阅的对象...

但要强调 - 当多个线程订阅/取消订阅对象时,这只是一个问题,这是:罕见,而b:不完全合乎需要。


12
2018-04-08 07:20



一个很好但又精确的解释:o) - Neil Knight
请注意,你可以 总是 最终会针对认为已取消订阅的处理程序引发事件 - 因为它可能在您开始执行委托之后但在您到达取消订阅之前取消订阅。 - Jon Skeet
所以这里的关键事实(针对提问者的合理问题,即委托是引用类型)是“委托是不可变的”,所以这个任务“捕获[es] a 快照 代表“。对吧? - AakashM
我认为不可改变意味着它不会改变自己。不明白为什么把它分配给变量的原因。 - Petr
@Petr - 你抓住了 当前 委托字段的值。当代码订阅/取消订阅时,它 取代 这有一个 不同 参考(可能是 null)。 - Marc Gravell♦


(从我在Essential C#4.0中读到的内容)

基本上,从这个C#代码:

public class CustomEventArgs: EventArgs {…}
public delegate void CustomEventHandler(object sender, CustomEventArgs a);
public event CustomEventHandler RaiseCustomEvent;

编译器将生成等效于以下C#代码的CIL代码(松散):

public delegate void CustomEventHandler(object sender, CustomEventArgs a);

private CustomEventHandler customEventHandler; // <-- generated by the compiler

public void add_CustomEventHandler(CustomEventHandler handler) {
  System.Delegate.Combine(customEventHandler, handler);
}

public void remove_CustomEventHandler(CustomEventHandler handler) {
  System.Delegate.Remove(customEventHandler, handler);
}

public event CustomEventHandler customEventHandler {
  add { add_customEventHandler(value) }
  remove { remove_customEventHandler(value) }
}

复制事件时,实际上是复制了 private CustomEventHandler customEventHandler。由于委托是不可变的,因此原件时副本不会受到影响 customEventHandler 被修改了。您可以尝试使用此代码来查看我的意思:

string s1 = "old"; 
string s2 = s1; 
s1 = "new"; // s2 is still "old"

关于生成的CIL的另一个重要特征 代码就是CIL相当于的 event 关键字仍保留在CIL中。 换句话说,事件是CIL代码识别的事物 明确;它不仅仅是一个C#构造。通过保持等价物 event CIL代码中的关键字,所有语言和编辑都能够提供 特殊功能,因为他们可以将事件识别为特殊事件 班级成员。

我猜你很困惑,主要是因为你认为事件是一个类的糖语法,对吧?


2
2017-10-04 10:39