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

标签 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

最佳答案

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

背景

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

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

Target 类型为 WeakReference 的委托(delegate)是不可能的.有所谓的Weak Event Pattern但它在 EventManager 之上实现而不是普通代表。

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

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

comObject.SomeEvent += EventCallback;

comObject 对象隐含地持有对 SomeClass 对象的强引用。 无论您使用何种订阅技术,无论 ComObject 是否为 COM 对象包装器,都是如此。

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

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

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

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

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

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

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

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

私有(private)的一些类一些类;

公共(public) SomeClaas() { //这与存储委托(delegate)基本相同 //在 comEventHandler 字段中 一些类=这个;

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

结论

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

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

    // GOOD!
    comObject.SomeEvent += new ComEventHandler(EventCallback);

    // EVEN BETTER!
    comObject.SomeEvent += EventCallback;

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

关于c# - 将事件分配给事件处理程序的两种不同类型的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13567692/

相关文章:

c# - 从 C++ 创建 COM 对象?

windows - 连续的接口(interface)函数 ID

javascript - 移动 Safari 上 JavaScript websocket 中的明显阻塞行为

c++ - Regsvr32 在 Windows 7 上崩溃

c# - 使 C#/.NET 占用空间小?

c# - 多线程任务表单调用问题,有更好的方法吗?

c# - 在单独的线程中启动 WPF 窗口

c# - 带有 Azure Functions 的 DocumentDB

powershell - 如何在 powershell 中发送鼠标点击?

events - 信号和槽 vs. 事件和事件监听器