我认为这两个代码片段是相同的,我们在这里没有任何关于强/弱引用的问题。
背景
首先,如果我们的Interop.ComObjectWrapper
提供了CLR事件(即存储事件处理程序的委托),我们肯定会从ComObjectWrapper
获得一个强引用到我们的对象。
任何委托都包含两部分:Target
类型为object
和指向特定方法的方法指针。如果Target
为null
,则回调指向静态方法。
不可能有一个带有Target
类型为WeakReference的委托。有所谓的弱事件模式,但它是在EventManager上实现的,而不是普通的委托。
将事件处理程序存储在字段中也无济于事。第一部分
内部事件实现意味着在订阅事件之后:
comObject.SomeEvent += EventCallback;
comObject
对象隐式地持有对SomeClass
对象的强引用。无论使用什么样的订阅技术以及ComObject是否是COM对象包装器,这都是真实的。
订阅事件会在生命周期方面隐式地增加两个对象之间的依赖关系。这就是为什么在.NET世界中,最常见的内存泄漏是由于订阅长时间存在的对象的事件而导致的。只要事件持有者在应用程序中可访问,事件订阅者就不会死亡。
将事件处理程序存储在字段中也没有帮助。第二部分
但是,即使我的假设不成立,ComObjectWrapper
提供了某种弱事件模式的概念,将事件处理程序保存在字段中也没有任何帮助。
让我们回顾一下事件关键字的含义:
private event ComEventHandler comEventHandler;
...
comEventHandler = new ComEventHandler(EventCallback);
将回调函数保存在当前字段中(基本上我们可以将私有事件视为简单的委托字段),不会改变现有行为。
我们已经知道,委托是一个简单的对象,用于存储对目标对象(即SomeClass对象)和方法(即public void EventCallBack())的引用。这意味着在字段中存储附加的委托会向SomeClass本身添加对SomeClass的附加引用。
基本上,在字段中存储事件处理程序在语义上等同于在SomeClass中存储附加引用:
private SomeClass someClass;
public SomeClaas()
{
//这与在comEventHandler字段中存储委托基本相同
someClass = this;
}
在SomeClass中存储强引用不会延长当前对象的生命周期。这意味着如果ComObjectWrapper没有保持对SomeClass对象的强引用,则在comEventHandler中存储事件处理程序不会延长SomeClass的生命周期,并且不会防止SomeClass被垃圾回收。
结论
在私有字段中存储事件处理程序不会延长对象的生命周期,也不会防止其被垃圾回收。
这就是以下代码片段在对象生命周期方面没有区别的原因:
comObject.SomeEvent += new ComEventHandler(EventCallback);
comObject.SomeEvent += EventCallback;
comEventHandler = new ComEventHandler(EventCallback);
comObject.SomeEvent += comEventHandler
comEventHandler
是一个“事件”,这让人感到困惑。如果将comEventHandler
设置为字段,代码会更易读和更简单。甚至可以将其设置为只读字段。 - Jeppe Stig Nielsen