已释放对象的行为

本文关键字:对象 释放 | 更新日期: 2023-09-27 17:57:00

我有一个带有IDisposable接口的类。现在我不知道我应该实施什么行为。应该在 Dispose 方法之后为此类中的每个方法调用抛出一个ObjectDisposedException,还是应该只在指定的方法(如对已释放资源的数据访问)中引发异常?

我测试了位图对象(只是示例):

Bitmap b = new Bitmap (100, 100);
b.Dispose (); // if i remove this line - console will display: Format32bppArgb
Console.WriteLine (b.PixelFormat);
Console.ReadKey ();

和控制台显示器:DontCare

所以没有例外。位图对象允许在我调用Dispose后使用PixelFormat属性。我应该遵循此行为吗?

已释放对象的行为

我在这个问题和许多其他问题上的哲学是"做有意义的事"。

在某些情况下,在类释放其资源后使用某些类成员可能是非常合理的。 实际上,某些方案可能需要此类使用。 例如,如果一个对象通过网络连接管理异步事务,则可以要求它关闭,然后在它这样做之后,询问它处理了多少事务,是否有任何事务处于悬空状态等。 在关闭完成之前,此类统计信息的最终值无法知道,并且从概念上讲,要求对象关闭然后要求它提供与它已经完成的事情相关的历史信息并没有错。

虽然有人可能会争辩说,Close应该关闭连接,同时允许使用报告历史信息的属性,而Dispose应该关闭事物并禁止使用此类属性,但我认为这样的区别是无益的。 除此之外,人们可能希望连接释放与其关联的所有资源(为了允许"重新打开"请求,Close可能会避免这样做)。 此外,在CloseDispose之间没有其他行为差异的情况下,我认为没有必要仅仅要求两种单独的方法Dispose以便可以使统计数据无效。

从某种意义上说,许多IDisposable对象可以被视为具有两个部分 - 与外部资源交互的实体,以及与托管代码交互且本身功能有限的实体。 虽然"关注点分离"原则建议这两个部分应该是单独的对象(事实上,当这种拆分可能会有所帮助时,存在尖齿),但在许多情况下,客户端代码将希望保存可以同时满足两个目的的单个引用。 该引用将不得不实现IDisposable,但处置不应该破坏托管代码方面的东西。

例如,请考虑 WinForms Font 类。 该类封装了两件事:(1) 有关字体(字样、大小、样式等)的信息集合,以及 (2) GDI 字体句柄。 当Font Dispose d时,它不能再用于绘制文本,但它不会忘记字体,样式等。 给定一个Dispose d字体,可以使用旧字体中的该信息构建新字体。 不幸的是,大多数允许读取此类信息的属性都被Dispose明确失效,这意味着在许多情况下,如果想要生成一种类似于现有但已处置Font但有一些更改的字体,则必须使用从旧字体复制的信息构建新字体, 基于该字体构造另一种新字体,然后Dispose创建的第一个新字体。 拥有一个包含与 typestyle 等相关的信息的 FontDescription 类可能会有所帮助,这样在想要保存字体说明但不需要 GDI 句柄的情况下,字体说明可以存储在非一次性类中,但这不是类的设计方式。

是否应该只在指定方法(例如对已释放资源的数据访问)中引发异常?

这是自动的,具有终结器的类应该抛出这样的情况。 毕竟,该方法将访问不再活动的操作系统对象,这将产生错误。 最好用像ObjectDisposedException这样的明确报告,而不是由操作系统错误代码产生的神秘问题。

您给出的位图示例是一个非常令人遗憾的示例,但对于 GDI+ 类来说并不少见。 它们通常具有非常差的错误处理。 不要让这成为一个例子。

上一段的关键词是"具有终结器的类"。 你的类不应该有一个终结器,所以你是否把自己扔掉而不是把它留给你封装的一次性类中的方法,这是值得商榷的。 一般来说,你应该避免它,它往往会让你的代码混乱,几乎没有真正的好处。 但是,如果您包装了一个像 Bitmap 这样返回错误数据的笨拙类,请随意这样做。

调用 dispose 后,将object设置为 null 是我通常遵循的方法。然后,您不需要创建任何异常,因为将抛出空异常,这似乎是正确的方法。

当一个对象为 null 时,它是否为 null 并不重要,因为它被释放了;或者它是 null,因为它没有初始化,或者它是 null,因为它被显式设置为 null。消费者应该知道它是空的,而不是空的基础操作。