如果 .ctor() 抛出,是否会调用 Dispose()?

Will Dispose() be called if a .ctor() throws?

我有一个 class,其中一个 IDisposable 成员变量在线初始化,另一个 IDisposable 在构造函数中。

如果构造函数抛出,是否会调用Dispose()?如果是这样,那么我认为 null 检查是必要的......?如果不是,那么内联成员如何处置?

    sealed class SomeDisposable : IDisposable { ... }

    sealed class Foo : IDisposable
    {
        readonly SomeDisposable sd1= new SomeDisposable(); // this doesn't throw
        readonly SomeDisposable sd2;
        public Foo()
        {
            sd2 = new SomeDisposable(); // assume this throws
            // how does sd1 get Dispose()d?
        }

        public void Dispose()
        {
            sd1.Dispose();
            if (sd2!= null) // is this null check necessary?
                sd2.Dispose();
        }
    }

假设您在表单中使用以下代码:

var foo = new Foo(someByteArray);

并且您的构造函数抛出异常,然后 foo 将为空,因为 class 构造函数未完成。任何调用它的尝试 Dispose 都会导致 NRE 发生。

有趣的问题。

试过这个:

try
{
  //caller
  using (var x = new Disposable1()) { }
}
catch (Exception ex)
{
   Debug.WriteLine(ex.Message);
}


public class Disposable1 : IDisposable
{
    private Disposable2 first = new Disposable2();
    private Disposable2 second;

    public Disposable1()
    {
        second = new Disposable2("Goodbye!");
    }

    public void Dispose()
    {
        Debug.WriteLine("Disposable1.Dispose()");

        first.Dispose();
        if (second != null)
            second.Dispose();
    }
}

public class Disposable2 : IDisposable
{
    public string Whatever { get; set; }

    public Disposable2() { Whatever = "Hello!"; }
    public Disposable2(string whatever)
    {
        Whatever = whatever;
        throw new Exception("Doh!");
    }

    public void Dispose()
    {
        Debug.WriteLine("Disposable2.Dispose()" + Whatever);
    }
}

...输出是...

Doh!

所以看起来确实没有初始化或释放成员。

目前在 C# 中没有办法使用内联初始化程序安全地初始化 IDisposable,除非通过涉及 ThreadStatic 变量的讨厌的黑客攻击。必须通过创建处置管理器对象并将引用存储在线程静态字段中的工厂方法调用构造函数。然后,字段初始值设定项可以将它们的值包装在对静态方法的调用中,该方法会将它们添加到处置管理器对象中。

实际的字段初始化器语法最终非常合理:

DisposalManager Cleaner = DisposalManager.CurrentManager;
DType1 DField1 = DisposalManager.Guard(new DType1);
DType2 DField2 = DisposalManager.Guard(new DType2);

Dispose 清理也是如此:

void Dispose(bool disposing)
{
  Cleaner.Cleanup(disposing);
}

不幸的是,每次调用 Guard 都需要自己访问线程静态字段,并且需要将所有构造函数调用包装在工厂方法中,这使得构造变得相当难看。太糟糕了,因为能够使用一行来声明、创建和清理字段比必须在代码中的三个不同位置执行这些操作要好得多。