我正在阅读.Net参考源,并在第408行的ButtonBase.cs中找到了这个gem :
bool exceptionThrown = true;
try
{
OnClick();
exceptionThrown = false;
}
finally
{
if (exceptionThrown)
{
// Cleanup the buttonbase state
SetIsPressed(false);
ReleaseMouseCapture();
}
}
Run Code Online (Sandbox Code Playgroud)
问题在于,什么会激励某人使用exceptionThrown
旗帜而不是将其写为
try
{
OnClick();
}
catch
{
SetIsPressed(false);
ReleaseMouseCapture();
throw;
}
Run Code Online (Sandbox Code Playgroud)
它只是风格还是有一些我不知道的副作用?
Eri*_*sch 11
这段代码的原因有两个.是的,格雷格提到,它不需要重新抛出异常..但这不是真正的原因.
真正的原因是语义之一.不应使用例外来处理控制流.这样做是为了处理这样一个事实:如果一个异常被抛出一个按钮,它可以将按钮视觉状态保持为"按下"状态.这并不是真正"处理"异常.这仅仅是在抛出异常时纠正视觉问题.
此代码不关心异常是什么,并且它不希望捕获所有异常,因为这是不好的做法.此外,代码没有做任何异常......它只是说"嘿,如果我们到达函数的末尾,那么我们都很好.如果我们没有,那么让我们重置按钮状态只是为了确定".
因此,这不是真正的异常处理,因此它不会捕获异常.它只是注意到抛出异常并进行一些清理.
编辑:
这种方法可能不那么有争议,如果只是像这样重命名,删除对异常的任何引用,那就更有意义了:
bool cleanupRequired = true;
try
{
OnClick();
cleanupRequired = false;
}
finally
{
if (cleanupRequired)
{
// Cleanup the buttonbase state
SetIsPressed(false);
ReleaseMouseCapture();
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:
为了支持我在下面的评论,我编写了以下测试程序来测试场景:
static void Main(string[] args)
{
TimeSpan ts = new TimeSpan();
TimeSpan ts2 = new TimeSpan();
TimeSpan ts3 = new TimeSpan();
TimeSpan ts4 = new TimeSpan();
TimeSpan ts5 = new TimeSpan();
TimeSpan ts6 = new TimeSpan();
TimeSpan ts7 = new TimeSpan();
TimeSpan ts8 = new TimeSpan();
Stopwatch sw = new Stopwatch();
// throw away first run
for (int i = 0; i < 2; i++)
{
sw.Restart();
try
{
throw new NotImplementedException();
}
catch
{
ts = sw.Elapsed;
}
sw.Stop();
ts2 = sw.Elapsed;
try
{
sw.Restart();
try
{
throw new NotImplementedException();
}
finally
{
ts3 = sw.Elapsed;
}
}
catch
{
ts4 = sw.Elapsed;
}
sw.Stop();
ts5 = sw.Elapsed;
try
{
sw.Restart();
try
{
throw new NotImplementedException();
}
catch
{
ts6 = sw.Elapsed;
throw;
}
}
catch
{
ts7 = sw.Elapsed;
}
sw.Stop();
ts8 = sw.Elapsed;
}
Console.WriteLine(ts);
Console.WriteLine(ts2);
Console.WriteLine(ts3);
Console.WriteLine(ts4);
Console.WriteLine(ts5);
Console.WriteLine(ts6);
Console.WriteLine(ts7);
Console.WriteLine(ts8);
Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)
我得到了以下结果(我将它们分开以使它们更容易阅读):
00:00:00.0028424
00:00:00.0028453
Run Code Online (Sandbox Code Playgroud)
00:00:00.0028354
00:00:00.0028401
00:00:00.0028427
Run Code Online (Sandbox Code Playgroud)
00:00:00.0028404
00:00:00.0057907
00:00:00.0057951
Run Code Online (Sandbox Code Playgroud)
最后3个显示当使用throw;
它重新抛出异常时不会简单地传递现有异常,它必须重新创建异常并重新抛出它,花费两倍的时间.
我们可以看到,捕获异常和捕获之间没有显着差异,但使用finally.但是,重新抛出异常是成本的来源.
这是在VS 2012 Update 3中运行的.
编辑:
没有调试器的计时.如您所见,重新抛出仍然是昂贵的两倍:
00:00:00.0000149
00:00:00.0000154
Run Code Online (Sandbox Code Playgroud)
00:00:00.0000137
00:00:00.0000140
00:00:00.0000146
Run Code Online (Sandbox Code Playgroud)
00:00:00.0000137
00:00:00.0000248
00:00:00.0000251
Run Code Online (Sandbox Code Playgroud)
如果你使用try,那就像必须抛出两次异常一样.与尝试,最后只有一个异常被抛出所以它是很多更有效,特别是如果这个代码被调用频繁.
显然,至少在 .Net 2 中仍然会删除堆栈跟踪信息,这会产生副作用throw;
。我想这个语法是用来解决throw;
框架早期版本的实现的。
这篇博客文章提供了两个示例,说明如何throw;
不等于根本不捕获异常。
显示重新抛出的异常的堆栈跟踪,而不是来自抛出点的堆栈跟踪的问题提供了一个场景,其中重新抛出异常会导致 Visual Studio 中的不同操作。
归档时间: |
|
查看次数: |
290 次 |
最近记录: |