【问题标题】:Question regarding to value/reference type of events关于事件的价值/参考类型的问题
【发布时间】:2010-04-08 07:16:29
【问题描述】:

在 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));
}

【问题讨论】:

    标签: c# events event-handling


    【解决方案1】:

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

    当然,它确实有 other 副作用,您可以(相反)最终针对认为它已经取消订阅的对象引发事件......

    但要强调的是,这只是在多个线程订阅/取消订阅对象时才会出现的问题,a:很少见,b:不完全可取。

    【讨论】:

    • 一个很好但准确的解释:o)
    • 请注意,您可以总是针对认为它已取消订阅的处理程序引发事件 - 因为它可以在您开始执行委托之后但在您之前取消订阅到达了取消订阅的那个。
    • 所以这里的关键事实(针对提问者的合理问题,即委托是引用类型)是“委托是不可变的”,所以这个赋值“捕获[es]一个快照 的代表”。对吗?
    • 我认为不可变意味着它不会改变自己。不明白为什么将其分配给变量的原因。
    • @Petr - 你捕获了委托字段的 current 值。当代码订阅/取消订阅时,它用一个不同的引用替换 this(很可能是null)。
    【解决方案2】:

    (来自我在 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 的另一个需要注意的重要特征 代码是 event 关键字的 CIL 等效项保留在 CIL 中。 换句话说,事件是 CIL 代码可以识别的东西 明确的;它不仅仅是一个 C# 构造。通过保留等效的event CIL 代码中的关键字,所有语言和编辑器都能够提供 特殊功能,因为他们可以将事件识别为特殊 班级成员。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-06
      • 2015-01-13
      • 1970-01-01
      • 1970-01-01
      • 2015-01-08
      • 2015-03-24
      • 1970-01-01
      相关资源
      最近更新 更多