捕获Using块的目标对象的构造函数中抛出的异常
using(SomeClass x = new SomeClass("c:/temp/test.txt"))
{
...
}
在 using 块内,一切正常,可以正常处理异常。但是如果 SomeClass
的构造函数可以抛出异常怎么办?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
将您的使用放入 try catch fe
Put your using into the try catch f.e.
是的,当构造函数抛出异常时,这就会出现问题。 您所能做的就是将 using 块包装在 try/catch 块中。 这就是为什么您必须这样做。
using 块只是语法糖,编译器将每个 using 块替换为等效的 try/finally 块。唯一的问题是编译器不会将构造函数包装在 try 块中。编译后的代码将在 IL 中进行以下转换。
从代码中可以看出,如果构造函数抛出异常,则对象 x 将不会被实例化,并且如果不处理,控件将不会从异常引发点进一步移动。
我刚刚发布了 昨晚在我的博客上发表了关于此主题的博客文章。
编辑
我想我找到了为什么 C# 不将对象构造包装到生成的 try 块中来代替 using 块的答案。
原因很简单。如果将声明和实例化都包装在 try 块中,则该对象将超出后续的 finally 块的范围,并且代码将无法编译,因为对于 finally 块,该对象几乎不存在。如果您仅将构造包装在 try 块中并在 try 块之前保留声明,即使在这种情况下,它也不会编译,因为它发现
您正在尝试使用分配的变量
。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. 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. The only issue is that the compiler does not wrap the constructor within the try block. Your code after compilation would have following conversion in the IL.
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.
I have just posted a blog-post on my blog on this subject last night.
EDIT
I think I found the answer why C# does not wrap object construction into try block generated in place of the using block.
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. 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 findsyou're trying to use an assigned variable
.我编写了一个快速测试程序来检查这一点,当构造函数中抛出异常时,Dispose 方法似乎不会被调用;
输出 :
如果你不抛出异常..
输出:
大概这是因为该对象从未创建,所以没有什么可以调用 Dispose 的。
我不确定如果构造函数已经分配了一些通常需要通过 Dispose 进行适当清理的资源,但随后发生了异常,会发生什么。
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;
Output :
If you don't throw the exception..
Output :
Presumably this is because the object was never created, so there's nothing to call Dispose on.
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.
对于一个设计良好的类来说这不应该是一个问题。请记住总体问题:
因此,问题是,在抛出异常之前,您应该如何处理
HoldsResources
构造函数分配的资源?答案是,您不应该对这些资源做任何事情。那不是你的工作。当决定
HoldsResources
将持有资源时,就产生了正确处置资源的义务。这意味着构造函数中的 try/catch/finally 块,并且意味着正确实现 IDisposable 来在 Dispose 方法中处置这些资源。您的责任是在使用完实例后使用
using
块调用他的Dispose
方法。没有别的了。This should not be a problem with a well-designed class. Remember the overall question:
So, the question is, what should you do about the resources allocated by the
HoldsResources
constructor before it threw an exception?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. That means a try/catch/finally block in the constructor, and it means proper implementation ofIDisposable
to dispose of those resources in theDispose
method.Your responsibility is to use the
using
block to call hisDispose
method when you're through using the instance. Nothing else.当您在 ctor 中获得了不受垃圾回收影响的资源时,您必须确保在出现问题时处置它们。
此示例显示了一个在出现问题时可以防止泄漏的 ctor,当您在工厂方法内分配一次性资源时,也适用相同的规则。
编辑:我不得不将我的样本从工厂更改为演员,因为显然它并不像我希望的那么容易理解。 (从评论来看。)
当然原因是:当你调用一个工厂或一个ctor时,你只能处理它的结果。当电话接通时,您必须假设到目前为止一切正常。
当给演员或工厂打电话时,你不必做任何逆向精神分析来处理任何你无论如何也拿不到的东西。如果它确实抛出异常,则工厂/ctor有责任在重新抛出异常之前清除所有半分配的内容。
(希望这一次足够详细......)
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.
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.
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. (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. If it does throw an exception, it is in the factories/ctor's responsibility to clear anything half-allocated before re-throwing the exception.
(Hope, this time, it was elaborate enough...)