3

我在 SO 中看到了这个示例代码,它说一种做法不好,另一种做法很好。但我不明白为什么?事实上,我得到了那个著名的 RCW COM 对象错误,而那个帖子说这可能是一个原因。

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 对象

4

1 回答 1

4

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

背景

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

任何委托都包含两部分:Target类型object和指向特定方法的方法指针。If Targetis nullthan 回调指向静态方法。

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

在现场存储事件处理程序将无济于事。第1部分

内部事件实现是指订阅事件后:

comObject.SomeEvent += EventCallback;

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

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

在现场存储事件处理程序将无济于事。第2部分

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

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

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

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

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

基本上,将事件处理程序存储在语义上等同于在 SomeClass 中存储附加引用:

私人 SomeClass someClass;

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

在 中存储强引用SomeClass不会延长当前对象的生命周期。这意味着 ifComObjectWrapper不会持有对SomeClass存储事件处理程序的对象的强引用,comEventHandler不会延长 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
于 2012-11-27T10:16:00.310 回答