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

时间:2015-04-14 00:24:13

标签: c# constructor exception-handling idisposable

我有一个类,其中一个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();
        }
    }

3 个答案:

答案 0 :(得分:1)

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

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

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自己访问线程静态字段,并且让所有构造函数调用都包含在工厂方法中,这使构造变得相当丑陋。太糟糕了,因为能够使用单行声明,创建和清理字段比在代码中的三个不同位置执行这些操作要好得多。

答案 1 :(得分:0)

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

var foo = new Foo(someByteArray);

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

答案 2 :(得分:0)

有趣的问题。

试过这个:

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!

所以似乎没有成员被初始化或处置。