通过处理&QUOT抛出的异常;配置"而平仓嵌套"采用"声明
显然,一些例外可能只是迷失在使用语句中使用嵌套的。考虑一个简单的控制台应用程序:
Apparently, some exceptions may just get lost while using nested using
statement. Consider this simple console app:
using System;
namespace ConsoleApplication
{
public class Throwing: IDisposable
{
int n;
public Throwing(int n)
{
this.n = n;
}
public void Dispose()
{
var e = new ApplicationException(String.Format("Throwing({0})", this.n));
Console.WriteLine("Throw: {0}", e.Message);
throw e;
}
}
class Program
{
static void DoWork()
{
// ...
using (var a = new Throwing(1))
{
// ...
using (var b = new Throwing(2))
{
// ...
using (var c = new Throwing(3))
{
// ...
}
}
}
}
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
{
// this doesn't get called
Console.WriteLine("UnhandledException:", e.ExceptionObject.ToString());
};
try
{
DoWork();
}
catch (Exception e)
{
// this handles Throwing(1) only
Console.WriteLine("Handle: {0}", e.Message);
}
Console.ReadLine();
}
}
}
当它得到处置的每个实例投掷
抛出。 AppDomain.CurrentDomain.UnhandledException
不会被调用。
Each instance of Throwing
throws when it gets disposed of. AppDomain.CurrentDomain.UnhandledException
never gets called.
输出:
Throw: Throwing(3)
Throw: Throwing(2)
Throw: Throwing(1)
Handle: Throwing(1)
我preFER至少能够登录失踪投掷(2)
和投掷(3)
。 我如何做到这一点,而不诉诸一个单独的的try / catch
每个使用
(这有点杀的便利使用
)?
在现实生活中,这些对象往往是在这我管不着类的实例。他们可能会或可能不会被抛出,但如果他们这样做,我想有一个选项来观察这种例外。
In real life, those objects are often instances of classes over which I have no control. They may or may not be throwing, but in case they do, I'd like to have an option to observe such exceptions.
这问题就来了半天我一直在寻找减少嵌套层次使用
。有一个简洁答案暗示聚集例外。有意思的是如何从嵌套的的标准行为使用
语句不同。
This question came along while I was looking at reducing the level of nested using
. There's a neat answer suggesting aggregating exceptions. It's interesting how this is different from the standard behavior of nested using
statements.
这个问题似乎是密切相关的:
Should你实现IDisposable.Dispose(),所以它不会抛出?
This question appears to be closely related: Should you implement IDisposable.Dispose() so that it never throws?
你所注意到在的Dispose
和的设计的一个基本问题使用
,对于没有很好的解决方案尚未存在。恕我直言,最好的设计是有一个版本的接收它可能正在申请(任何异常作为参数的Dispose
或空
,如果没有挂起),并且可以记录或封装了异常,如果它需要抛出自己的之一。否则,如果您同时拥有code的控制权,可能导致内的异常使用
以及在的Dispose
,您可以使用某种外部的数据通道让的Dispose
了解内部异常,但是这是相当做作。
What you are noticing is a fundamental problem in the design of Dispose
and using
, for which no nice solution as yet exists. IMHO the best design would be to have a version of Dispose
which receives as an argument any exception which may be pending (or null
, if none is pending), and can either log or encapsulate that exception if it needs to throw one of its own. Otherwise, if you have control of both the code which could cause an exception within the using
as well as within the Dispose
, you may be able to use some sort of outside data channel to let the Dispose
know about the inner exception, but that's rather hokey.
这太糟糕了有一个为code显式或隐式的通过与
),以了解是否有相关的最后
块关联(使用不恰当的语言支持尝试
正确完成,如果没有,哪里出了问题。该的Dispose
应失败默默的想法是恕我直言非常危险的,判断错误。如果一个对象封装了一个文件,该文件是开放的写作和的Dispose
关闭文件(一个常见的模式),数据不能被写入,具有处置
调用返回通常会导致调用code相信数据被正确地写入,可能允许它覆盖唯一的好备份。此外,如果文件都应该是明确关闭,并要求的Dispose
而不关闭一个文件应该被认为是一个错误,这将意味着的Dispose
应该抛出一个异常,如果受保护块否则会完全正常,但如果把守块失败的调用关闭
,因为首先发生异常,有的Dispose
抛出一个异常,将是非常无益的。
It's too bad there's no proper language support for code associated with a finally
block (either explicitly, or implicitly via using
) to know whether the associated try
completed properly and if not, what went wrong. The notion that Dispose
should silently fail is IMHO very dangerous and wrongheaded. If an object encapsulates a file which is open for writing, and Dispose
closes the file (a common pattern) and the data cannot be written, having the Dispose
call return normally would lead the calling code to believe the data was written correctly, potentially allowing it to overwrite the only good backup. Further, if files are supposed to be closed explicitly and calling Dispose
without closing a file should be considered an error, that would imply that Dispose
should throw an exception if the guarded block would otherwise complete normally, but if the guarded block fails to call Close
because an exception occurred first, having Dispose
throw an exception would be very unhelpful.
如果表现并不重要,你可以在VB.NET编写一个包装方法,它会接受两位代表(类型动作
和动作<异常>
),调用中的尝试
块,然后调用第二个在最后第一code>与发生在
尝试
块除外(如果有的话)阻塞。如果包装方法写在VB.NET,它可以发现并没有报告所发生的异常的有赶上并重新抛出。其它图案将是可能的。包装的大多数应用将涉及关闭,这是恶心,但包装至少可以达到正确的语义。
If performance isn't critical, you could write a wrapper method in VB.NET which would accept two delegates (of types Action
and an Action<Exception>
), call the first within a try
block, and then call the second in a finally
block with the exception that occurred in the try
block (if any). If the wrapper method was written in VB.NET, it could discover and report the exception that occurred without having to catch and rethrow it. Other patterns would be possible as well. Most usages of the wrapper would involve closures, which are icky, but the wrapper could at least achieve proper semantics.
这是另一种包装设计,能避免倒闭,但需要客户端正确地使用它,并会提供针对不正确使用的保护很少会有一个使用连击,如:
An alternative wrapper design which would avoid closures, but would require that clients use it correctly and would provide little protection against incorrect usage would have a usage batter like:
var dispRes = new DisposeResult();
...
try
{
.. the following could be in some nested routine which took dispRes as a parameter
using (dispWrap = new DisposeWrap(dispRes, ... other disposable resources)
{
...
}
}
catch (...)
{
}
finally
{
}
if (dispRes.Exception != null)
... handle cleanup failures here
这种方法的问题是,有没有办法,以确保任何人都不会评估 DIS pres.Exception
。人们可以使用一个终结登录的情况下,其中 DIS preS
被遗弃而无需进行了检查,但就没有办法区分在哪里发生,因为异常情况踢code出超越如果
测试,或者是因为程序员只是忘了检查。
The problem with this approach is that there's no way to ensure that anyone will ever evaluate dispRes.Exception
. One could use a finalizer to log cases where dispRes
gets abandoned without ever having been examined, but there would be no way to distinguish cases where that occurred because an exception kicked code out beyond the if
test, or because the programmer simply forgot the check.
PS - 另一种情况,其中的Dispose
真的应该知道是否异常发生时的IDisposable
对象用来包装一个对象的不变量可能会暂时失效,但预计锁或其他范围为code离开之前范围恢复。如果发生异常,code应该经常有没有的解析的异常期待,但仍应的采取行动的基于它,留下没有举行,也没有释放锁,但而无效的,因此任何present或未来收购它会抛出一个异常尝试。如果没有未来尝试获取锁或其他资源,但事实上,它是无效的,不应干扰系统的正常运行。如果资源是极其必要的程序的某些部分,无效就会造成的那部分程序死亡,同时尽量减少它对任何其他的损害。我知道真正落实这种情况下,漂亮的语义,唯一的方法是使用封恶心。否则,唯一的办法是要求明确的无效/验证调用,并希望code,其中的资源是通过调用pceded无效,则$ P $来验证部分内的任何return语句。
PS--Another case where Dispose
really should know whether exceptions occur is when IDisposable
objects are used to wrap locks or other scopes where an object's invariants may temporarily be invalidated but are expected to be restored before code leaves the scope. If an exception occurs, code should often have no expectation of resolving the exception, but should nonetheless take action based upon it, leaving the lock neither held nor released but rather invalidated, so that any present or future attempt to acquire it will throw an exception. If there are no future attempts to acquire the lock or other resource, the fact that it is invalid should not disrupt system operation. If the resource is critically necessary to some part of the program, invalidating it will cause that part of the program to die while minimizing the damage it does to anything else. The only way I know to really implement this case with nice semantics is to use icky closures. Otherwise, the only alternative is to require explicit invalidate/validate calls and hope that any return statements within the part of the code where the resource is invalid are preceded by calls to validate.