什么时候检查该对象是否已被释放并抛出 ObjectDisposeException 是合理的?
在实现IDisposable
的类中,什么时候检查对象是否已被释放并抛出ObjectDisposeException
(如果已释放)是合理的?在所有公共方法和属性中(Dispose
除外)?有时?绝不?
In a class that implements IDisposable
, when is it reasonable to check if the object has been disposed and throw ObjectDisposedException
if it has? In all public methods and properties (except Dispose
)? Sometimes? Never?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您应该仅在不适用于已处置对象的方法中实现该检查。
例如:
如果您的类在
Dispose
中关闭数据库连接或文件句柄,则所有需要这些数据库连接或文件句柄的方法都需要检查实例是否已公开。You should implement that check only in methods that don't work on a disposed object.
For example:
If your class closes the database connections or file handles in
Dispose
, all methods that need those database connections or file handles need to check if the instance is already exposed.如果一个对象支持 IsDispose,则该方法本身永远不应该抛出异常;如果 IsDispose 返回 true,许多其他方法抛出异常是正确的,但异常应该由这些方法而不是 IsDispose 生成。人们可能有一个实用方法 AssertNotDispose,如果对象被释放,它就会抛出异常,但具有该名称的方法会出现这种行为。
否则,我建议在很多情况下,让一个对象保存 IDisposable 对象,并能够在保持有用状态的同时释放内部对象是有用的。例如,一个对象的功能是显示和维护一个无模式对话框以从用户那里获取信息,即使在对话框关闭后也可能有效地保留字段内容的副本。这样的对象应该提供一个“Close”方法,该方法将处理内部的 Disposable 对象,但保持有用的状态。虽然它也可以有一个 Dispose 方法,该方法会调用 Close,但也会设置一个“NoLongerValid”标志,这会导致字段属性抛出,但我认为这不会真正增加任何价值。
我承认,在许多情况下,一个对象在被处理后可以保持有用的状态,这表明一个类可能应该被拆分。例如,Font 类也许应该分为非一次性 FontInfo 类(保存字体的描述,但不包含 GDI 句柄)和 IDisposable ReadyFont 类(继承 FontInfo,并封装 GDI 字体对象)。使用字体的例程可以检查给定的对象是 FontInfo 还是 ReadyFont;在前一种情况下,他们可以创建 GDI 字体,使用它,然后发布它;在后一种情况下,他们可以使用 ReadyFont 的 GDI 字体对象并释放它。 ReadyFont 的创建者将负责确保其清理。
事实上,我不知道系统在渲染控件时是否会尝试使用与控件的 Font 属性关联的 GDI 对象,但我知道如果 Font 被 Dispose,它不会发出警告(即使它之前已被 Dispose)将其分配给 Font 属性!)。如有必要,控件当然能够创建新的 GDI 字体;我不知道他们是否总是创建一种新的 GDI 字体,还是仅在旧字体被处理后才这样做。前一种行为似乎性能更高,但除非仔细编码,否则如果一个线程在另一个线程正在使用字体时尝试释放字体,则可能会导致问题。
If an object supports IsDisposed, that method itself should never throw; it would be proper for many other methods to throw if IsDisposed returns true, but the exception should be generated by those methods rather than by IsDisposed. One might have a utility method AssertNotDisposed which would throw if an object is disposed, but such behavior would be expected from a method with that name.
Otherwise, I would suggest that there are many cases where it's useful to have an object hold IDisposable object, and be able to Dispose the inner object while maintaining useful state. For example, an object whose function is to show and maintain a modeless dialog box to get information from a user might usefully keep a copy of the contents of the fields even after the box is closed. Such an object should provide a "Close" method which will Dispose the inner Disposable objects but maintain useful state. While it could also have a Dispose method which would call Close but also set a "NoLongerValid" flag that would cause field properties to throw, I don't think that would really add any value.
I will grant that many cases where an object can hold useful state after it has been disposed are indicative of a class which should perhaps be split. For example, the Font class should perhaps be split into a non-disposable FontInfo class (holding a description of a font, but not a GDI handle) and an IDisposable ReadyFont class (inheriting FontInfo, and encapsulating a GDI font object). Routines that use a font could check whether the object they were given was a FontInfo or a ReadyFont; in the former case, they could create a GDI font, use it, and release it; in the latter case, they could use the ReadyFont's GDI font object and release it. The creator of a ReadyFont would then be responsible for ensuring its cleanup.
As it is, I don't know if the system will try to use the GDI object associated with a control's Font property when rendering the control, but I know that it doesn't squawk if the Font is Disposed (even if it's Disposed before assigning it to the Font property!). Controls are certainly capable of creating new GDI fonts if necessary; I don't know whether they always create a new GDI font or if they only do so if the old one has been disposed. The former behavior would seem to be more performant, but unless coded carefully could cause problems if one thread tried to Dispose a Font while another thread was using it.
唯一真正指定的是 public void Dispose() 本身不应该抛出任何东西。
任何需要(非)托管资源的方法都应该抛出。
在我看来,只剩下一些有争议的情况:
当类组合像 Stream 和 Collection 这样的(不相关的)函数时,抛出就会变得更加困难。我们只是不应该这样做。
The only thing that is really specified is that
public void Dispose()
itself should not throw anything.And any method that needs the (un)managed resources should throw.
That leaves, in my mind, only a few disputable cases:
It becomes more difficult when the class combines (unrelated) functions like a Stream and a Collection. We just shouldn't do that.
使用已处置的对象是您希望尽快找到的编程错误。
检查的地方越多,发现错误的速度就越快。
您绝对应该检查它,其中已处理的对象将导致某种其他类型的异常(即空指针),以免使用户感到困惑。
在其他地方,这取决于应用程序是否值得付出努力。
Using a disposed object is a programming error you want to find as fast as possible.
The more places you check it the faster you will find the error.
You should definitely check it where a disposed object will cause some other kind of exception (i.e. null pointer) to not confuse the user.
In other places it depends on the application if it is worth the effort.
正如您所说,我将在除 Dispose 和 IsDispose 之外的所有公共方法和属性中实现此检查。这是标准模式,以防止开发人员在使用您的类型时会误以为它仍然有效。
As you say, I would implement this check in all public methods and properties except Dispose and IsDisposed. This is the standard pattern to prevent a developer from using your type in the mistaken impression that it's still valid.