简体   繁体   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"))
{
...
}

Inside the using block, all is fine with treating exceptions as normal. 在使用块内,一切正常,正常处理异常。 But what if the constructor of SomeClass can throw an exception? 但是,如果SomeClass的构造函数可以抛出异常怎么办?

Put your using into the try catch fe 把你的使用放入try catch fe

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

Yes , this will be a problem when the constructor throws an exception. 是的 ,当构造函数抛出异常时,这将是一个问题。 All you can do is wrap the using block within a try/catch block. 您所能做的就是将try块包装在try / catch块中。 Here's why you must do it that way. 这就是你必须这样做的原因。

using blocks are just syntactic sugar and compiler replaces each using block with equivalent try/finall block. 使用块只是语法糖,编译器使用等效的try / finall块替换每个块。 The only issue is that the compiler does not wrap the constructor within the try block. 唯一的问题是编译器没有在try块中包装构造函数。 Your code after compilation would have following conversion in the IL. 编译后的代码将在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();
        }

As you can see from the code, the object x will not be instantiated in case when the constructor throws an exception and the control will not move further from the point of exception raise if not handled. 从代码中可以看出,如果构造函数抛出异常并且控件在未处理的情况下不会从异常点的进一步移动,则不会实例化对象x。

I have just posted a blog-post on my blog on this subject last night. 我昨晚刚刚在我的博客上发布了关于这个主题的博客文章

I'm just now wondering why C# designers did not wrap object construction within the try block which according to me should have been done. 我现在想知道为什么C#设计师没有在try块中包装对象构造,根据我应该这样做。

EDIT 编辑

I think I found the answer why C# does not wrap object construction into try block generated in place of the using block. 我想我找到了答案为什么C#没有将对象构造包装成代替using块生成的try块。

The reason is simple. 原因很简单。 If you wrap both declaration and instantiation within the try block then the object would be out of scope for the proceeding finally block and the code will not compile at because, for finally block the object hardly exists. 如果你在try块中包含声明和实例化,那么the object would be out of scope for the proceeding finally block并且代码将不会编译,因为对于finally块,对象几乎不存在。 If you only wrap the construction in the try block and keep declaration before the try block, even in that case the it will not compile since it finds you're trying to use an assigned variable . 如果你只在try块中包含构造并在try块之前保持声明,即使在那种情况下它也不会编译,因为它发现you're trying to use an assigned variable

I threw a quick test program together to check this, and it seems that the Dispose method does not get called when an exception is thrown in the constructor; 我一起扔了一个快速测试程序来检查这个,似乎在构造函数中抛出异常时不会调用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");
    }
}

Output : 输出:

Some Error! 有些错误!

If you don't throw the exception.. 如果你不抛出异常..

Output : 输出:

Everything is fine 一切都好

I've disposed my resources 我已经处理了我的资源

Presumably this is because the object was never created, so there's nothing to call Dispose on. 大概这是因为从未创建过对象,所以没有什么可以调用Dispose。

I'm not sure what would happen if the constructor had already allocated some resources which would normally require a proper clean up through Dispose and the exception occurred afterwards though. 我不确定如果构造函数已经分配了一些资源,通常需要通过Dispose进行适当的清理,然后发生异常,会发生什么。

This should not be a problem with a well-designed class. 对于精心设计的类,这不应该是一个问题。 Remember the overall question: 记住整体问题:

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

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

So, the question is, what should you do about the resources allocated by the HoldsResources constructor before it threw an exception? 所以,问题是,在抛出异常之前,你应该如何处理HoldsResources构造函数分配的资源?

The answer is, you shouldn't do anything about those resources. 答案是,你不应该对这些资源做任何事情 That's not your job. 那不是你的工作。 When it was decided that HoldsResources would hold resources, that brought the obligation to properly dispose of them. 当决定HoldsResources持有资源时,就有义务妥善处置它们。 That means a try/catch/finally block in the constructor, and it means proper implementation of IDisposable to dispose of those resources in the Dispose method. 这意味着构造函数中的try / catch / finally块,这意味着正确实现IDisposable以在Dispose方法中处理这些资源。

Your responsibility is to use the using block to call his Dispose method when you're through using the instance. 您的责任是在使用实例时使用using块来调用他的Dispose方法。 Nothing else. 没有其他的。

When you get a hold of resources in the ctor that are not subject to garbage collection, you have to make sure to dispose of them when things go south. 当您在ctor中获得不受垃圾收集限制的资源时,您必须确保在事情发生时将其丢弃。

This sample shows a ctor which will prevent a leak when something goes wrong, the same rules apply when you allocate disposables inside a factory method. 此示例显示了一个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;
    }
  }
}

Edit : I had to change my sample from a factory to a ctor, because apparantly it wasn't as easy to understand as I hoped it would be. 编辑 :我不得不将我的样品从工厂改为ctor,因为它显然不像我希望的那样容易理解。 (Judging from the comments.) (从评论来看。)

And of course the reason for this is: When you call a factory or a ctor, you can only dispose of its result. 当然,原因是:当你打电话给工厂或者工厂时,你只能处理它的结果。 When the call goes through, you have to assume that everything's okay so far. 当电话通过时,你必须假设到目前为止一切都还好。

When calling a ctor or factory you don't have to do any reverse-psychoanalysis to dispose of anything you can't get hold of anyways. 在打电话给ctor或工厂时,你不需要做任何逆向精神分析来处理任何你无法控制的东西。 If it does throw an exception, it is in the factories/ctor's responsibility to clear anything half-allocated before re-throwing the exception. 如果它确实抛出异常,那么在重新抛出异常之前,工厂/ ctor有责任清除半分配的任何东西。 (Hope, this time, it was elaborate enough...) (希望,这一次,它足够精心......)

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

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