将两种不同类型的事件分配给事件处理程序的区别

我在SO中看到了这个示例代码,它说一个练习很糟糕而另一个很好。 但我不明白为什么? 事实上,我得到了那个着名的RCW COM对象错误,该post说这可能是一个原因。

public class SomeClass { private Interop.ComObjectWrapper comObject; private event ComEventHandler comEventHandler; public SomeClass() { comObject = new Interop.ComObjectWrapper(); // NO - BAD! comObject.SomeEvent += new ComEventHandler(EventCallback); // YES - GOOD! comEventHandler = new ComEventHandler(EventCallback); comObject.SomeEvent += comEventHandler } public void EventCallback() { // DO WORK } 

}

编辑:这是源的链接: 不能使用已与其基础RCW分离的COM对象

我认为这两个代码片段是相同的,我们在这里没有强/弱引用的任何问题。

背景

首先,如果我们的Interop.ComObjectWrapper提供了CLR事件(即在委托中存储事件处理程序的事件),我们肯定会从ComObjectWrapper到我们的对象获得强引用。

任何委托包含两部分:类型object和特定方法的方法指针。 如果Targetnull ,则回调指向静态方法。

不可能有一个类型为WeakReference的 Target的委托。 有一种所谓的弱事件模式,但它实现在EventManager之上,而不是普通的委托。

在字段中存储事件处理程序将无济于事。 第1部分

内部事件实现意味着在订阅事件之后:

 comObject.SomeEvent += EventCallback; 

comObject对象隐含地拥有对SomeClass对象的强引用。 无论您使用何种订阅技术以及ComObject是否是COM对象包装器,这都是正确的。

订阅事件会在生命周期中添加两个对象之间的隐式依赖关系。 这就是为什么.NET世界中最常见的内存泄漏是由订阅长期对象的事件引起的。 在应用程序中可访问事件持有者之前,事件订阅者不会死

在字段中存储事件处理程序将无济于事。 第2部分

但事件如果我的假设不正确并且ComObjectWrapper提供了一些弱事件模式的概念,那么在字段中保存事件处理程序将无济于事。

让我们回顾一下event关键字的含义:

 private event ComEventHandler comEventHandler; ... comEventHandler = new ComEventHandler(EventCallback); 

在当前字段中保存回调(基本上我们可以将私有事件视为简单的委托字段)不会更改现有行为。

我们已经知道委托是一个简单的对象,它存储对Target对象(即SomeClass对象)和方法(即public void EventCallBack() )的public void EventCallBack() 。 这意味着在字段中存储附加委托会从SomeClass本身添加对SomeClass附加引用。

基本上,在字段中存储事件处理程序在语义上等同于在SomeClass中存储其他引用:

private SomeClass someClass;

public SomeClaas(){//这与在comEventHandler字段中存储委托//基本相同someClass = this; }

SomeClass存储强引用不会延长当前对象的生命周期。 这意味着如果ComObjectWrapper不能保存对SomeClass对象的强引用,那么在SomeClass存储事件处理程序将不会延长SomeClass的生命周期,也不会阻止SomeClass进行垃圾回收。

结论

在私有字段中存储事件处理程序不会延长对象的生命周期,也不会阻止它进行垃圾回收。

这就是为什么以下代码片段在对象生存期方面没有区别:

  // GOOD! comObject.SomeEvent += new ComEventHandler(EventCallback); // EVEN BETTER! comObject.SomeEvent += EventCallback; // NOT GOOD, BECAUSE WAN'T HELP! comEventHandler = new ComEventHandler(EventCallback); comObject.SomeEvent += comEventHandler