繁体   English   中英

捕获在Using块的目标对象的构造函数中抛出的异常

[英]Catching exceptions thrown in the constructor of the target object of a Using block

using(SomeClass x = new SomeClass("c:/temp/test.txt"))
{
...
}

在使用块内,一切正常,正常处理异常。 但是,如果SomeClass的构造函数可以抛出异常怎么办?

把你的使用放入try catch fe

try
{
   using(SomeClass x = new SomeClass("c:/temp/test.txt"))
   {
       ...
   }
}
catch(Exception ex)
{
   ...
}

是的 ,当构造函数抛出异常时,这将是一个问题。 您所能做的就是将try块包装在try / catch块中。 这就是你必须这样做的原因。

使用块只是语法糖,编译器使用等效的try / finall块替换每个块。 唯一的问题是编译器没有在try块中包装构造函数。 编译后的代码将在IL中进行以下转换。

        //Declare object x of type SomeClass.
        SomeClass x;

        //Instantiate the object by calling the constructor.
        x = new SomeClass("c:/temp/test.txt");

        try
        {
            //Do some work on x.
        }
        finally
        {
            if(x != null)
                x.Dispose();
        }

从代码中可以看出,如果构造函数抛出异常并且控件在未处理的情况下不会从异常点的进一步移动,则不会实例化对象x。

我昨晚刚刚在我的博客上发布了关于这个主题的博客文章

我现在想知道为什么C#设计师没有在try块中包装对象构造,根据我应该这样做。

编辑

我想我找到了答案为什么C#没有将对象构造包装成代替using块生成的try块。

原因很简单。 如果你在try块中包含声明和实例化,那么the object would be out of scope for the proceeding finally block并且代码将不会编译,因为对于finally块,对象几乎不存在。 如果你只在try块中包含构造并在try块之前保持声明,即使在那种情况下它也不会编译,因为它发现you're trying to use an assigned variable

我一起扔了一个快速测试程序来检查这个,似乎在构造函数中抛出异常时不会调用Dispose方法;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            using (OtherClass inner = new OtherClass())
            {
                Console.WriteLine("Everything is fine");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        Console.Read();
    }
}

class OtherClass : IDisposable
{
    public OtherClass()
    {
        throw new Exception("Some Error!");
    }

    void IDisposable.Dispose()
    {
        Console.WriteLine("I've disposed my resources");
    }
}

输出:

有些错误!

如果你不抛出异常..

输出:

一切都好

我已经处理了我的资源

大概这是因为从未创建过对象,所以没有什么可以调用Dispose。

我不确定如果构造函数已经分配了一些资源,通常需要通过Dispose进行适当的清理,然后发生异常,会发生什么。

对于精心设计的类,这不应该是一个问题。 记住整体问题:

public class HoldsResources : IDisposable
{
    public HoldsResources()
    {
        // Maybe grab some resources here
        throw new Exception("whoops");
    }
}

using (HoldsResources hr = new HoldsResources())
{
}

所以,问题是,在抛出异常之前,你应该如何处理HoldsResources构造函数分配的资源?

答案是,你不应该对这些资源做任何事情 那不是你的工作。 当决定HoldsResources持有资源时,就有义务妥善处置它们。 这意味着构造函数中的try / catch / finally块,这意味着正确实现IDisposable以在Dispose方法中处理这些资源。

您的责任是在使用实例时使用using块来调用他的Dispose方法。 没有其他的。

当您在ctor中获得不受垃圾收集限制的资源时,您必须确保在事情发生时将其丢弃。

此示例显示了一个ctor,它可以在出现问题时防止泄漏,当您在工厂方法中分配一次性使用时,同样的规则也适用。

class Sample
{
  IDisposable DisposableField;

  ...

  public Sample()
  {
    var disposable = new SomeDisposableClass();
    try
    {
       DoSomething(disposable);
       DisposableField = disposable;
    }
    catch
    {
       // you have to dispose of it yourself, because
       // the exception will prevent your method/ctor from returning to the caller.
       disposable.Dispose();
       throw;
    }
  }
}

编辑 :我不得不将我的样品从工厂改为ctor,因为它显然不像我希望的那样容易理解。 (从评论来看。)

当然,原因是:当你打电话给工厂或者工厂时,你只能处理它的结果。 当电话通过时,你必须假设到目前为止一切都还好。

在打电话给ctor或工厂时,你不需要做任何逆向精神分析来处理任何你无法控制的东西。 如果它确实抛出异常,那么在重新抛出异常之前,工厂/ ctor有责任清除半分配的任何东西。 (希望,这一次,它足够精心......)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM