1

这个问题是关于处理非托管资源(COM 互操作)并确保不会有任何资源泄漏。对于我是否似乎以正确的方式做事的反馈,我将不胜感激。

背景:


假设我有两个课程:

  • 一个类LimitedComResource,它是 COM 对象的包装器(通过某些 API 接收)。这些 COM 对象的数量有限,因此我的类实现了IDisposable接口,该接口将负责在不再需要 COM 对象时释放它。

  • 临时创建另一种类型ManagedObject的对象以在LimitedComResource. 他们不是IDisposable

用图表总结以上内容,我的类可能如下所示:

            +---------------+           +--------------------+
            | ManagedObject | <>------> | LimitedComResource |
            +---------------+           +--------------------+
                                                  |
                                                  o IDisposable

(稍后我将提供这两个类的示例代码。)

问题:


由于我的临时ManagedObject物品不是一次性的,我显然无法控制它们会存在多长时间。但是,与此同时,我可能会知道 a所指的Disposed 。我如何确保 a不会访问不再存在的 a?LimitedComObjectManagedObjectManagedObjectLimitedComResource

            +---------------+           +--------------------+
            | managedObject | <>------> |   (dead object)    |
            +---------------+           +--------------------+

我目前已经通过混合弱引用和一个标志来实现这一点,该标志LimitedResource表明一个对象是否已经被释放。有没有更好的办法?

示例代码(我目前拥有的):


LimitedComResource

class LimitedComResource : IDisposable
{
    private readonly IUnknown comObject;  // <-- set in constructor

    ...

    void Dispose(bool notFromFinalizer)
    {
        if (!this.isDisposed)
        {
            Marshal.FinalReleaseComObject(comObject);
        }
        this.isDisposed = true;
    }

    internal bool isDisposed = false;
}

ManagedObject

class ManagedObject
{
    private readonly WeakReference limitedComResource;  // <-- set in constructor

    ...

    public void DoSomeWork()
    {
        if (!limitedComResource.IsAlive())
        {
            throw new ObjectDisposedException();
            //        ^^^^^^^^^^^^^^^^^^^^^^^
            //  is there a more suitable exception class?
        }

        var ur = (LimitedComResource)limitedComResource.Target;
        if (ur.isDisposed)
        {
            throw new ObjectDisposedException();
        }

        ...  // <-- do something sensible here!
    }
}
4

2 回答 2

1

当您将弱引用的目标转换为对象类型时,如果对象已被释放,它将返回 null。只需检查您返回的值是否为空,然后再对其执行操作。请参阅文档中的示例。您还可以找到这篇关于使用弱引用的文章。这是后一篇文章的相关引述:

要建立强引用并再次使用对象,请将 WeakReference 的 Target 属性强制转换为对象的类型。如果 Target 属性返回 null,则表示该对象已被收集;否则,您可以继续使用该对象,因为应用程序已重新获得对它的强引用。

例子:

class ManagedObject 
{ 
    private readonly WeakReference limitedComResource;  // <-- set in constructor 

    ... 

    public void DoSomeWork() 
    { 
        var ur = (LimitedComResource)limitedComResource.Target; 
        if (ur == null) 
        { 
            throw new ObjectDisposedException(); 
        } 

        ...  // <-- do something sensible here! 
    } 
}
于 2010-04-29T21:56:14.443 回答
1

不,这不行。WeakReference 仅告诉您托管对象已被垃圾收集。这与 IDisposable 无关。Dispose() 的目的是在垃圾收集器执行此操作之前释放非托管资源。

事实上,如果托管对象在 gen #1 中而 COM 包装器在 gen #0 中,则会出现严重问题。WeakReference 不能使包装器保持活动状态,它将被收集并在您有机会自己调用 Dispose() 之前处置 COM 对象。

只需在托管对象中存储对包装对象的普通引用。您可以在调用 Dispose() 后将其设置为 null,以便收集包装器。如果客户端代码尝试使用它并且引用为空,则抛出 ObjectDisposedException。或者重新创建它,如果这有意义的话。

于 2010-04-30T00:59:13.360 回答