问题 一次捕获多个异常?
简单地抓住它是不鼓励的 System.Exception
。相反,只应捕获“已知”异常。
现在,这有时会导致不必要的重复代码,例如:
try
{
WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
WebId = Guid.Empty;
}
catch (OverflowException)
{
WebId = Guid.Empty;
}
我想知道:有没有办法捕获两个例外,只能调用 WebId = Guid.Empty
打电话一次?
给出的例子相当简单,因为它只是一个 GUID
。但想象一下你多次修改一个对象的代码,如果其中一个操作以预期的方式失败,你想要“重置” object
。但是,如果出现意外异常,我仍然希望将其提高。
10269
2017-09-25 20:56
起源
答案:
抓住 System.Exception
并打开类型
catch (Exception ex)
{
if (ex is FormatException || ex is OverflowException)
{
WebId = Guid.Empty;
return;
}
throw;
}
1792
2017-09-25 21:01
编辑: 我同意其他人的观点,从C#6.0开始,异常过滤器现在是一个非常好的方法: catch (Exception ex) when (ex is ... || ex is ... )
除了我仍然讨厌一个长线布局,并将个人像下面那样放置代码。我认为这是功能性的,因为它是审美的,因为我相信它可以提高理解力。有些人可能不同意:
catch (Exception ex) when (
ex is ...
|| ex is ...
|| ex is ...
)
原版的:
我知道我在这里参加聚会有点晚了,但圣烟......
直接追逐,这种复制更早的答案,但如果你真的想要为几种异常类型执行一个共同的操作,并保持整个事情在一个方法的范围内整洁,为什么不只是使用lambda / closure / inline函数执行以下操作?我的意思是,很有可能你最终会意识到你只想让那个闭包成为一个可以在各地利用的独立方法。但是,如果不在结构上实际更改代码的其余部分,那么这将非常容易。对?
private void TestMethod ()
{
Action<Exception> errorHandler = ( ex ) => {
// write to a log, whatever...
};
try
{
// try some stuff
}
catch ( FormatException ex ) { errorHandler ( ex ); }
catch ( OverflowException ex ) { errorHandler ( ex ); }
catch ( ArgumentNullException ex ) { errorHandler ( ex ); }
}
我不禁想知道(警告: 前面有点讽刺/讽刺)为什么在地球上去做所有这些努力基本上只是替换以下内容:
try
{
// try some stuff
}
catch( FormatException ex ){}
catch( OverflowException ex ){}
catch( ArgumentNullException ex ){}
...这个下一个代码气味的一些疯狂的变化,我的意思是,只是假装你节省了一些按键。
// sorta sucks, let's be honest...
try
{
// try some stuff
}
catch( Exception ex )
{
if (ex is FormatException ||
ex is OverflowException ||
ex is ArgumentNullException)
{
// write to a log, whatever...
return;
}
throw;
}
因为它肯定不会自动更具可读性。
当然,我离开了三个相同的实例 /* write to a log, whatever... */ return;
在第一个例子中。
但这是我的观点。你们都听说过功能/方法,对吧?认真。写一个共同点 ErrorHandler
函数,以及从每个catch块调用它。
如果你问我,第二个例子(带有 if
和 is
在项目的维护阶段,关键字的可读性明显较低,同时也更容易出错。
维护阶段,对于任何可能相对较新的编程人员来说,将占项目整体生命周期的98.7%或更多,而做维护的穷人也几乎肯定会成为除你以外的其他人。并且他们很有可能将50%的时间花在诅咒你名字的工作上。
当然,FxCop会咆哮你,所以你必须这样做 也在代码中添加一个属性,该属性具有与正在运行的程序完全相同的zip,并且只是告诉FxCop忽略一个问题,在99.9%的情况下,它在标记中是完全正确的。而且,对不起,我可能会弄错,但是那个“忽略”属性最终实际编译到你的应用程序中了吗?
会把整个 if
在一行上测试使它更具可读性?我不这么认为。我的意思是,我确实让另一位程序员在很久以前激烈地争辩说,将更多代码放在一行上会使其“运行得更快”。但当然,他是疯狂的坚果。试图向他解释(直言不讳 - 这很有挑战性)解释器或编译器如何将这条长线分开为离散的单指令每行语句 - 如果他已经取得了进展,那么基本上与结果相同只是使代码可读而不是试图超越编译器 - 对他没有任何影响。但我离题了。
多少 减 从现在起一个月或两个月再添加三种异常类型时,这是否可以实现? (答案:它有一个 批量 不太可读)。
实际上,其中一个重点是,我们每天都在查看文本源代码的大部分格式是让其他人真正,非常明显地在代码运行时实际发生了什么。因为编译器将源代码转换为完全不同的东西,并且对代码格式化风格无关紧要。所以一对一的线路也很糟糕。
只是说......
// super sucks...
catch( Exception ex )
{
if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
{
// write to a log, whatever...
return;
}
throw;
}
370
2017-10-12 00:24
正如其他人所指出的,你可以拥有一个 if
在catch块中的语句来确定发生了什么。 C#6支持异常过滤器,因此以下内容将起作用:
try { … }
catch (Exception e) when (MyFilter(e))
{
…
}
该 MyFilter
方法可能看起来像这样:
private bool MyFilter(Exception e)
{
return e is ArgumentNullException || e is FormatException;
}
或者,这可以全部内联完成(when语句的右侧只需要是一个布尔表达式)。
try { … }
catch (Exception e) when (e is ArgumentNullException || e is FormatException)
{
…
}
这与使用不同 if
来自内部的声明 catch
阻止,使用异常过滤器 将不会 放松堆栈。
你可以下载 Visual Studio 2015 检查一下。
如果要继续使用Visual Studio 2013,可以安装以下nuget包:
安装包Microsoft.Net.Compilers
在撰写本文时,这将包括对C#6的支持。
引用此包将导致使用
包含在C#和Visual Basic编译器中的特定版本
包,而不是任何系统安装版本。
241
2018-04-04 13:59
不幸的是,不是在C#中,因为你需要一个例外过滤器来做这件事而C#不公开MSIL的那个功能。 VB.NET确实具有这种能力,例如
Catch ex As Exception When TypeOf ex Is FormatException OrElse TypeOf ex Is OverflowException
你可以做的是使用匿名函数来封装你的错误代码,然后在那些特定的catch块中调用它:
Action onError = () => WebId = Guid.Empty;
try
{
// something
}
catch (FormatException)
{
onError();
}
catch (OverflowException)
{
onError();
}
184
2017-09-25 21:03
为了完整起见,从那时起 .NET 4.0代码可以重写为:
Guid.TryParse(queryString["web"], out WebId);
的TryParse 永远不会抛出异常,如果格式错误则返回false,将WebId设置为 Guid.Empty
。
以来 C#7 您可以避免在单独的行上引入变量:
Guid.TryParse(queryString["web"], out Guid webId);
您还可以创建用于解析返回元组的方法,这些方法在.NET Framework中从4.6版开始不可用:
(bool success, Guid result) TryParseGuid(string input) =>
(Guid.TryParse(input, out Guid result), result);
并像这样使用它们:
WebId = TryParseGuid(queryString["web"]).result;
// or
var tuple = TryParseGuid(queryString["web"]);
WebId = tuple.success ? tuple.result : DefaultWebId;
当在C#12中实现out-parameters的解构时,接下来对这个无用的答案进行无用的更新。:)
123
2018-04-13 12:18
如果您可以将您的应用程序升级到C#6,那么您很幸运。新的C#版本已经实现了Exception过滤器。所以你可以这样写:
catch (Exception ex) when (ex is FormatException || ex is OverflowException) {
WebId = Guid.Empty;
}
有些人认为这段代码是一样的
catch (Exception ex) {
if (ex is FormatException || ex is OverflowException) {
WebId = Guid.Empty;
}
throw;
}
但事实并非如此。实际上,这是C#6中唯一不能在先前版本中模拟的新功能。首先,重新投掷意味着比跳过捕获更多的开销。其次,它在语义上不相同。在调试代码时,新功能可以保持堆栈完好无损。如果没有此功能,崩溃转储就不那么有用甚至无用了。
看到 在CodePlex上讨论这个问题。和 示例显示差异。
62
2018-04-01 12:29
如果你不想使用 if
内部声明 catch
范围, 在 C# 6.0
您可以使用 Exception Filters
句法 CLR在预览版本中已经支持,但仅存在于 VB.NET
/MSIL
:
try
{
WebId = new Guid(queryString["web"]);
}
catch (Exception exception) when (exception is FormatException || ex is OverflowException)
{
WebId = Guid.Empty;
}
这段代码会抓住 Exception
只有当它是一个 InvalidDataException
要么 ArgumentNullException
。
实际上,你可以把任何条件基本放在里面 when
条款:
static int a = 8;
...
catch (Exception exception) when (exception is InvalidDataException && a == 8)
{
Console.WriteLine("Catch");
}
请注意,而不是 if
里面的声明 catch
的范围, Exception Filters
不能扔 Exceptions
,当他们这样做,或当条件不是时 true
, 下一个 catch
条件将被评估:
static int a = 7;
static int b = 0;
...
try
{
throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
Console.WriteLine("General catch");
}
输出:一般捕获。
什么时候有一个以上 true
Exception Filter
- 第一个将被接受:
static int a = 8;
static int b = 4;
...
try
{
throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
Console.WriteLine("General catch");
}
输出:捕获。
正如你在中看到的那样 MSIL
代码未翻译为 if
陈述,但是 Filters
,和 Exceptions
不能从标有的区域内扔出 Filter 1
和 Filter 2
但过滤器扔了 Exception
将失败,也就是最后一个比较值推到堆栈之前 endfilter
命令将确定过滤器的成功/失败(Catch 1
XOR Catch 2
将相应执行):

另外,具体而言 Guid
有 Guid.TryParse
方法。
26
2017-10-07 17:31
接受的答案似乎可以接受,但CodeAnalysis /的FxCop 会抱怨它正在捕捉一般的异常类型。
此外,似乎“是”运营商可能会略微降低性能。
CA1800:不要不必要地施放 说“考虑测试'as'运算符的结果”,但是如果你这样做,你将编写的代码比单独捕获每个异常的代码要多。
无论如何,这就是我要做的:
bool exThrown = false;
try
{
// Something
}
catch (FormatException) {
exThrown = true;
}
catch (OverflowException) {
exThrown = true;
}
if (exThrown)
{
// Something else
}
19
2017-07-30 17:09
这是Matt答案的变体(我觉得这有点干净)...使用方法:
public void TryCatch(...)
{
try
{
// something
return;
}
catch (FormatException) {}
catch (OverflowException) {}
WebId = Guid.Empty;
}
将抛出任何其他异常和代码 WebId = Guid.Empty;
不会被击中。如果您不希望其他异常导致程序崩溃,只需在其他两个捕获之后添加:
...
catch (Exception)
{
// something, if anything
return; // only need this if you follow the example I gave and put it all in a method
}
18
2017-08-31 20:51
@Micheal
您的代码略有修改版本:
catch (Exception ex)
{
Type exType = ex.GetType();
if (exType == typeof(System.FormatException) ||
exType == typeof(System.OverflowException)
{
WebId = Guid.Empty;
} else {
throw;
}
}
字符串比较是丑陋和缓慢的。
17
2017-09-25 21:01