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

时间:2012-11-26 15:15:18

标签: c# events com

我在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
    }

}

编辑:这是指向来源的链接:COM object that has been separated from its underlying RCW cannot be used

1 个答案:

答案 0 :(得分:4)

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

背景

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

任何委托包含两部分:类型Target的{​​{1}}和特定方法的方法指针。如果objectTarget,则回调指向静态方法。

无法拥有nullWeakReference的代理人。有一个所谓的Weak Event Pattern,但它实现在EventManager之上而不是普通代表。

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

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

Target

comObject.SomeEvent += EventCallback; 对象隐含地强烈引用comObject对象。 无论您正在使用何种订阅技术以及ComObject是否是COM对象包装器,都是如此。

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

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

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

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

ComObjectWrapper

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

我们已经知道委托是一个简单的对象,它存储对Target对象(即private event ComEventHandler comEventHandler; ... comEventHandler = new ComEventHandler(EventCallback); 对象)和方法(即SomeClass)的引用。这意味着在字段中存储其他委托会为public void EventCallBack()本身的SomeClass添加额外的引用。

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

private SomeClass someClass;

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

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

结论

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

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

comEventHandler