当构造失败时如何处置类字段

本文关键字:何处置 字段 失败 | 更新日期: 2023-09-27 18:16:20

假设我有一个类,它有两个字段,每个字段都包含一次性对象。

    class C : IDisposable {
            private IDisposable thing1 = new Thing1();
            private IDisposable thing2 = new Thing2();
               ... IDisposable implementation ...
    }

创建类C的实例时,将先创建Thing1,然后创建Thing2。但是,如果在构造Thing2时发生异常,则不会创建它,并且类C的实例也不会完成创建。实例化类C的代码没有对部分构造的C的引用来调用Dispose,所以我们留下了一个永远不会被处置的Thing1的实例。

最好的分辨率是多少?

当构造失败时如何处置类字段

您可以将初始化代码移动到构造函数中,然后将整个构造函数包装在一个try/catch块中,该块处理所有拥有的对象并重新抛出异常。

在c++/CLI中,可以通过单个声明指定如何定义、初始化和清除字段。在vb.net中,可以设计一个基类,使子类能够做同样的事情,例如

Dim MyBitmap as Bitmap = Acquire(new Bitmap(Params.FileName))

其中Params是一个对象,其中存储了一些构造函数参数(如FileName)。Acquire方法(在基类中定义)将把对新创建的IDisposable的引用添加到一个列表中,如果构造函数抛出该列表,则应该清理该列表。

不幸的是,c#不支持这两种模式。最接近的方法是定义一些静态方法,使用ThreadStatic字段来跟踪创建的对象,但这有点麻烦。在实践中,最好的方法可能是定义Dispose,以便正确地处理部分构造的对象,并让每个构造函数包含如下代码:
bool success = false;
try
{
  ...initialize stuff
  success = true;
}
finally
{
  if (!success)
    Dispose();
}

注意,因为Dispose()是虚的,它可能被调用多次。此外,由于子类Dispose方法可能在子类构造函数没有运行的情况下被调用,因此每个Dispose层都需要为这种可能性做好准备。