复杂对象图中 IDisposable 非托管资源的生命周期问题?
这个问题是关于处理非托管资源(COM 互操作)并确保不会出现任何资源泄漏。如果您能就我是否以正确的方式做事提供反馈,我将不胜感激。
背景:
假设我有两个类:
类
LimitedComResource
,它是 COM 对象的包装器(通过某些 API 接收)。这些 COM 对象的数量有限,因此我的类实现了IDisposable
接口,该接口将负责在不再需要时释放 COM 对象。临时创建另一种类型
ManagedObject
的对象,以在LimitedComResource
上执行某些工作。它们不是IDisposable
。
用图表总结以上内容,我的类可能如下所示:(
+---------------+ +--------------------+
| ManagedObject | <>------> | LimitedComResource |
+---------------+ +--------------------+
|
o IDisposable
稍后我将提供这两个类的示例代码。)
问题:
由于我的临时 ManagedObject
对象不是一次性的,我显然无法控制他们会存在多久。然而,与此同时,我可能已经Dispose
d了ManagedObject
所引用的LimitedComObject
。 如何确保 ManagedObject
不会访问不再存在的 LimitedComResource
?
+---------------+ +--------------------+
| 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!
}
}
This question is about dealing with unmanaged resources (COM interop) and making sure there won't be any resource leaks. I'd appreciate feedback on whether I seem to do things the right way.
Background:
Let's say I've got two classes:
A class
LimitedComResource
which is a wrapper around a COM object (received via some API). There can only be a limited number of those COM objects, therefore my class implements theIDisposable
interface which will be responsible for releasing a COM object when it's no longer needed.Objects of another type
ManagedObject
are temporarily created to perform some work on aLimitedComResource
. They are notIDisposable
.
To summarize the above in a diagram, my classes might look like this:
+---------------+ +--------------------+
| ManagedObject | <>------> | LimitedComResource |
+---------------+ +--------------------+
|
o IDisposable
(I'll provide example code for these two classes in just a moment.)
Question:
Since my temporary ManagedObject
objects are not disposable, I obviously have no control over how long they'll be around. However, in the meantime I might have Dispose
d the LimitedComObject
that a ManagedObject
is referring to. How can I make sure that a ManagedObject
won't access a LimitedComResource
that's no longer there?
+---------------+ +--------------------+
| managedObject | <>------> | (dead object) |
+---------------+ +--------------------+
I've currently implemented this with a mix of weak references and a flag in LimitedResource
which signals whether an object has already been disposed. Is there any better way?
Example code (what I've currently got):
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!
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不,这不行。 WeakReference 仅告诉您托管对象已被垃圾回收。这与 IDisposable 无关。 Dispose() 的目的是在垃圾收集器执行此操作之前释放非托管资源。
事实上,如果托管对象位于第 1 代中而 COM 包装器位于第 0 代中,则会遇到严重问题。 WeakReference 无法使包装器保持活动状态,在您有机会自己调用 Dispose() 之前,它将被收集并释放 COM 对象。
只需在托管对象中存储对包装器对象的简单引用即可。您可以在调用 Dispose() 后将其设置为 null,以便收集包装器。如果客户端代码尝试使用它并且引用为 null,则抛出 ObjectDisposeException。或者重新创建它,如果这有意义的话。
Nope, this is not okay. A WeakReference only tells you that a managed object got garbage collected. Which has nothing to do with IDisposable. The point of Dispose() is to release unmanaged resources before the garbage collector does it.
In fact, you got a serious problem if the managed object is in gen #1 and the COM wrapper is in gen #0. The WeakReference can't keep the wrapper alive, it will be collected and the COM object disposed before you got a chance to call Dispose() yourself.
Just store a plain reference to the wrapper object in your managed object. You can set it to null after you call Dispose() so that the wrapper can get collected. And throw ObjectDisposedException if the client code tries to use it and the reference is null. Or recreate it, if that makes sense.
当将弱引用的目标强制转换为对象类型时,如果该对象已被释放,它将返回 null。在执行操作之前,只需检查返回的值是否为 null。请参阅文档中的示例。您可能还会找到有关使用弱引用的文章。这是后一篇文章的相关引用:
例子:
When you cast the target of the weak reference to the object type, it will return null if the object has been disposed. Simply check to see if the value you get back is null before performing operations with it. See the example in the documentation. You might also find this article on Using Weak References of use. Here's a relevant quote from the latter article:
Example: