Catch multiple exceptions at once?
不鼓励仅仅抓住
现在,这有时会导致不必要的重复代码,例如:
1 2 3 4 5 6 7 8 9 10 11 12 | try { WebId = new Guid(queryString["web"]); } catch (FormatException) { WebId = Guid.Empty; } catch (OverflowException) { WebId = Guid.Empty; } |
我想知道:有没有一种方法可以同时捕获两个异常,并且只调用一次
给出的例子相当简单,因为它只是一个
捕获
1 2 3 4 5 6 7 8 9 10 |
编辑:我说的是谁的人认为,与concur,C滤波器的# 6.0,现在在perfectly异常精细的方式去:
除了我仍然恨的一种长的地图布局,个人的代码在下面的出样。我认为这是它的审美功能,提高阅读能力,因为我相信它。一些可能disagree:
原文:
我知道我在这里的小晚的派对,但神圣的烟幕。
直切的追逐,这一类duplicates早期的答案,但如果你真的想完成一些常见的异常类型和行动保持整洁和整洁的所有的东西的范围内的一个方法,为什么不只是使用内联函数λ/ /关闭到下面的什么样?我认为,机会是很好的意思,你只是想要实现你端上,使封闭的单独的方法,你可以使用所有的地方。然后它将是超级容易,没有实际的代码的其余部分在结构上的变化。好吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 ); } } |
我不能帮助,但奇迹(警告:超前小sarcasm反讽/)为什么在线地球去到所有这一切的努力,只是基本上是以下:
1 2 3 4 5 6 7 | try { // try some stuff } catch( FormatException ex ){} catch( OverflowException ex ){} catch( ArgumentNullException ex ){} |
这一变化...with一些疯狂的例子代码的气味,我均,仅仅假装是你节省几个键击。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
因为它更可读的肯定是不是自动的。
我认为,在相同的实例(左三
但这是我点的排序。你们有听过/方法的功能,对吗?(一)严重。写的函数和类,共
如果你问我,第二个实例(与
任何人谁是在维护阶段,可能是一个相对较新的方案,将包括进入98.7 %或以上的寿命,你的整个项目,和穷人的笨蛋做的几乎是肯定要维护的是你比其他人。有一个很好的机会与他们的,他们会花50%的时间在职诅咒你的名字。
当然,在与你和你barks FxCop也要添加属性到您的代码已经运行到与准确的压缩程序,和仅仅是一个有问题的忽视告诉FxCop是一例,它是在99.9%)在完全正确的标记。对不起,我可能是,但不是这样的mistaken,端上"忽略"属性为真的编译你的应用程序?
会把整个一个在线测试在线
这有多少不可读的程序,当你添加更多的异常类型的三个月,从现在或双吗?(答案:它有很多不可读)。
主要的一点,那是真的,大部分的点源代码的文本格式的,我们的每一天都看的是使它真的,真的很明显的对其他的人类,其实是什么发生当代码运行。因为编译器的源代码转成完全不同的东西和不在乎关于你的无格式的代码风格。一个操作系统的全网络地图全烂了。
只是说……
1 2 3 4 5 6 7 8 9 10 |
正如其他人所指出的,您可以在catch块中使用
1 2 3 4 5 | try { … } catch (Exception e) when (MyFilter(e)) { … } |
那么,
1 2 3 4 |
或者,可以全部以内联方式完成(when语句的右侧必须是布尔表达式)。
这与在
您可以下载Visual Studio 2015来查看。
如果要继续使用Visual Studio 2013,可以安装以下Nuget包:
Install-Package Microsoft.Net.Compilers
在写作时,这将包括对C 6的支持。
Referencing this package will cause the project to be built using the
specific version of the C# and Visual Basic compilers contained in the
package, as opposed to any system installed version.
不幸的是,不在C中,因为您需要一个异常过滤器来完成它,C不公开MSIL的功能。但是,vb.net确实具有这种功能,例如
您可以使用匿名函数来封装出错代码,然后在这些特定的catch块中调用它:
1 2 3 4 5 6 7 8 9 10 11 12 13 | Action onError = () => WebId = Guid.Empty; try { // something } catch (FormatException) { onError(); } catch (OverflowException) { onError(); } |
为了完整起见,由于.NET 4.0,代码可以重写为:
1 | Guid.TryParse(queryString["web"], out WebId); |
台盼从不抛出异常,如果格式错误,则返回false,将webid设置为
由于C 7,您可以避免在单独的行中引入变量:
1 | Guid.TryParse(queryString["web"], out Guid webId); |
您还可以创建用于分析返回的元组的方法,这些元组在.NET框架中从4.6版开始还不可用:
1 2 | (bool success, Guid result) TryParseGuid(string input) => (Guid.TryParse(input, out Guid result), result); |
像这样使用它们:
1 2 3 4 | WebId = TryParseGuid(queryString["web"]).result; // or var tuple = TryParseGuid(queryString["web"]); WebId = tuple.success ? tuple.result : DefaultWebId; |
当在C 12中实现out参数的解构时,对这个无用答案的下一个无用更新就会出现。:)
如果你能把你的应用升级到C 6,你就很幸运了。新的C版本实现了异常过滤器。所以你可以这样写:
1 2 3 |
有些人认为此代码与
1 2 3 4 5 6 |
但事实并非如此。实际上,这是C 6中唯一一个在以前的版本中无法模拟的新功能。首先,重新抛出意味着比跳过捕获更多的开销。第二,它在语义上不是等价的。在调试代码时,新特性会保持堆栈的完整性。如果没有这个特性,崩溃转储就不那么有用,甚至是无用的。
请参阅关于CodePlex的讨论。以及一个显示差异的例子。
异常过滤器现在在C 6+中可用。你可以做到
1 2 3 4 5 6 7 8 |
如果您不想在
1 2 3 4 5 6 7 8 |
只有当它是
实际上,你基本上可以在
1 2 3 4 5 6 7 8 | static int a = 8; ... catch (Exception exception) when (exception is InvalidDataException && a == 8) { Console.WriteLine("Catch"); } |
注意,与
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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"); } |
Output: General catch.
当有多个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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"); } |
Output: Catch.
正如你在
另外,具体来说,
使用C 7,可以提高Michael Stum的答案,同时保持switch语句的可读性:
1 2 3 4 5 6 7 8 9 10 11 12 | catch (Exception ex) { switch (ex) { case FormatException _: case OverflowException _: WebId = Guid.Empty; break; default: throw; } } |
接受的答案似乎是可以接受的,除了codeanalysis/fxcop会抱怨它捕获了一般的异常类型。
此外,似乎"is"运算符可能会稍微降低性能。
CA1800:不要不必要地强制转换,而是说"考虑测试‘as’操作符的结果",但是如果这样做,您将编写比单独捕获每个异常更多的代码。
不管怎样,我要做的是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | bool exThrown = false; try { // Something } catch (FormatException) { exThrown = true; } catch (OverflowException) { exThrown = true; } if (exThrown) { // Something else } |
在C 6中,建议的方法是使用异常过滤器,下面是一个示例:
1 2 3 4 5 6 7 8 9 |
这是马特答案的一个变种(我觉得这有点干净)…使用一种方法:
1 2 3 4 5 6 7 8 9 10 11 12 | public void TryCatch(...) { try { // something return; } catch (FormatException) {} catch (OverflowException) {} WebId = Guid.Empty; } |
任何其他异常都将被抛出,代码
1 2 3 4 5 6 | ... catch (Exception) { // something, if anything return; // only need this if you follow the example I gave and put it all in a method } |
JosephDaigle的答案是一个很好的解决方案,但是我发现下面的结构更整洁,不容易出错。
1 2 3 4 5 6 |
反转表达式有几个优点:
- 不需要返回语句
- 代码没有嵌套
- 不存在忘记"抛出"或"返回"语句的风险,在约瑟夫的解决方案中,这些语句与表达式分离。
它甚至可以压缩成一行(虽然不是很漂亮)
1 2 3 4 |
编辑:C 6.0中的异常过滤将使语法更加清晰,并且比任何当前解决方案都有许多其他好处。(最显著的是保持堆栈未损坏)
以下是使用C 6.0语法时的相同问题:
@米夏埃尔.沃尔什
稍微修改过的代码版本:
1 2 3 4 5 6 7 8 9 10 11 |
字符串比较既丑陋又缓慢。
怎么样
1 2 3 4 5 6 7 8 9 10 11 | try { WebId = Guid.Empty; WebId = new Guid(queryString["web"]); } catch (FormatException) { } catch (OverflowException) { } |
警告和警告:还有另一种,功能性的风格。
链接中的内容并不能直接回答您的问题,但是将其扩展为如下所示是很简单的:
1 2 3 4 5 6 7 8 | static void Main() { Action body = () => { ...your code... }; body.Catch<InvalidOperationException>() .Catch<BadCodeException>() .Catch<AnotherException>(ex => { ...handler... })(); } |
(基本上提供另一个空的
更大的问题是为什么。我不认为成本大于收益。)
1 2 3 4 5 6 7 8 9 10 |
更新2015-12-15:参见https://stackoverflow.com/a/22864936/1718702了解C_6。它是一种干净的语言,现在是标准的语言。
针对那些希望更优雅的解决方案捕获一次并过滤异常的人,我使用了如下所示的扩展方法。
我在我的库中已经有了这个扩展,最初是为其他目的编写的,但是它非常适合于
用法
1 2 3 4 5 6 7 8 |
isanyof.cs扩展(有关依赖项,请参阅完整的错误处理示例)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | namespace Common.FluentValidation { public static partial class Validate { /// <summary> /// Validates the passed in parameter matches at least one of the passed in comparisons. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="p_parameter">Parameter to validate.</param> /// <param name="p_comparisons">Values to compare against.</param> /// <returns>True if a match is found.</returns> /// <exception cref="ArgumentNullException"></exception> public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons) { // Validate p_parameter .CannotBeNull("p_parameter"); p_comparisons .CannotBeNullOrEmpty("p_comparisons"); // Test for any match foreach (var item in p_comparisons) if (p_parameter.Equals(item)) return true; // Return no matches found return false; } } } |
完整错误处理示例(复制粘贴到新控制台应用程序)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using Common.FluentValidation; namespace IsAnyOfExceptionHandlerSample { class Program { static void Main(string[] args) { // High Level Error Handler (Log and Crash App) try { Foo(); } catch (OutOfMemoryException ex) { Console.WriteLine("FATAL ERROR! System Crashing." + ex.Message); Console.ReadKey(); } } static void Foo() { // Init List<Action<string>> TestActions = new List<Action<string>>() { (key) => { throw new FormatException(); }, (key) => { throw new ArgumentException(); }, (key) => { throw new KeyNotFoundException();}, (key) => { throw new OutOfMemoryException(); }, }; // Run foreach (var FooAction in TestActions) { // Mid-Level Error Handler (Appends Data for Log) try { // Init var SomeKeyPassedToFoo ="FooParam"; // Low-Level Handler (Handle/Log and Keep going) try { FooAction(SomeKeyPassedToFoo); } catch (Exception ex) { if (ex.GetType().IsAnyOf( typeof(FormatException), typeof(ArgumentException))) { // Handle Console.WriteLine("ex was {0}", ex.GetType().Name); Console.ReadKey(); } else { // Add some Debug info ex.Data.Add("SomeKeyPassedToFoo", SomeKeyPassedToFoo.ToString()); throw; } } } catch (KeyNotFoundException ex) { // Handle differently Console.WriteLine(ex.Message); int Count = 0; if (!Validate.IsAnyNull(ex, ex.Data, ex.Data.Keys)) foreach (var Key in ex.Data.Keys) Console.WriteLine( "[{0}]["{1}" = {2}]", Count, Key, ex.Data[Key]); Console.ReadKey(); } } } } } namespace Common.FluentValidation { public static partial class Validate { /// <summary> /// Validates the passed in parameter matches at least one of the passed in comparisons. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="p_parameter">Parameter to validate.</param> /// <param name="p_comparisons">Values to compare against.</param> /// <returns>True if a match is found.</returns> /// <exception cref="ArgumentNullException"></exception> public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons) { // Validate p_parameter .CannotBeNull("p_parameter"); p_comparisons .CannotBeNullOrEmpty("p_comparisons"); // Test for any match foreach (var item in p_comparisons) if (p_parameter.Equals(item)) return true; // Return no matches found return false; } /// <summary> /// Validates if any passed in parameter is equal to null. /// </summary> /// <param name="p_parameters">Parameters to test for Null.</param> /// <returns>True if one or more parameters are null.</returns> public static bool IsAnyNull(params object[] p_parameters) { p_parameters .CannotBeNullOrEmpty("p_parameters"); foreach (var item in p_parameters) if (item == null) return true; return false; } } } namespace Common.FluentValidation { public static partial class Validate { /// <summary> /// Validates the passed in parameter is not null, throwing a detailed exception message if the test fails. /// </summary> /// <param name="p_parameter">Parameter to validate.</param> /// <param name="p_name">Name of tested parameter to assist with debugging.</param> /// <exception cref="ArgumentNullException"></exception> public static void CannotBeNull(this object p_parameter, string p_name) { if (p_parameter == null) throw new ArgumentNullException( string.Format("Parameter "{0}" cannot be null.", p_name), default(Exception)); } } } namespace Common.FluentValidation { public static partial class Validate { /// <summary> /// Validates the passed in parameter is not null or an empty collection, throwing a detailed exception message if the test fails. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="p_parameter">Parameter to validate.</param> /// <param name="p_name">Name of tested parameter to assist with debugging.</param> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static void CannotBeNullOrEmpty<T>(this ICollection<T> p_parameter, string p_name) { if (p_parameter == null) throw new ArgumentNullException("Collection cannot be null. Parameter_Name:" + p_name, default(Exception)); if (p_parameter.Count <= 0) throw new ArgumentOutOfRangeException("Collection cannot be empty. Parameter_Name:" + p_name, default(Exception)); } /// <summary> /// Validates the passed in parameter is not null or empty, throwing a detailed exception message if the test fails. /// </summary> /// <param name="p_parameter">Parameter to validate.</param> /// <param name="p_name">Name of tested parameter to assist with debugging.</param> /// <exception cref="ArgumentException"></exception> public static void CannotBeNullOrEmpty(this string p_parameter, string p_name) { if (string.IsNullOrEmpty(p_parameter)) throw new ArgumentException("String cannot be null or empty. Parameter_Name:" + p_name, default(Exception)); } } } |
两个样本nunit单元测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | using System; using System.Collections.Generic; using Common.FluentValidation; using NUnit.Framework; namespace UnitTests.Common.Fluent_Validations { [TestFixture] public class IsAnyOf_Tests { [Test, ExpectedException(typeof(ArgumentNullException))] public void IsAnyOf_ArgumentNullException_ShouldNotMatch_ArgumentException_Test() { Action TestMethod = () => { throw new ArgumentNullException(); }; try { TestMethod(); } catch (Exception ex) { if (ex.GetType().IsAnyOf( typeof(ArgumentException), /*Note: ArgumentNullException derrived from ArgumentException*/ typeof(FormatException), typeof(KeyNotFoundException))) { // Handle expected Exceptions return; } //else throw original throw; } } [Test, ExpectedException(typeof(OutOfMemoryException))] public void IsAnyOf_OutOfMemoryException_ShouldMatch_OutOfMemoryException_Test() { Action TestMethod = () => { throw new OutOfMemoryException(); }; try { TestMethod(); } catch (Exception ex) { if (ex.GetType().IsAnyOf( typeof(OutOfMemoryException), typeof(StackOverflowException))) throw; /*else... Handle other exception types, typically by logging to file*/ } } } } |
因为我觉得这些答案只是表面上的,所以我试图再深入一点。
所以我们真正想做的是一些不编译的事情,比如:
1 2 3 4 5 6 7 8 9 10 11 12 | // Won't compile... damn public static void Main() { try { throw new ArgumentOutOfRangeException(); } catch (ArgumentOutOfRangeException) catch (IndexOutOfRangeException) { // ... handle } |
之所以需要这样做,是因为我们不希望异常处理程序捕获稍后在流程中需要的内容。当然,我们可以捕捉到一个异常,并检查一下"如果"该怎么做,但老实说,我们并不真的想要这样做。(fxcop,调试器问题,丑陋)
那么,为什么这段代码不能编译呢?我们怎么能以这样的方式破解它呢?
如果我们看一下代码,我们真正想做的是转发呼叫。但是,根据MS分区II,IL异常处理程序块不会这样工作,在本例中这是有意义的,因为这意味着"exception"对象可以有不同的类型。
或者用代码编写,我们要求编译器做类似的事情(好吧,这并不完全正确,但这是我猜最接近的事情):
1 2 3 4 5 6 7 8 9 10 11 12 | // Won't compile... damn try { throw new ArgumentOutOfRangeException(); } catch (ArgumentOutOfRangeException e) { goto theOtherHandler; } catch (IndexOutOfRangeException e) { theOtherHandler: Console.WriteLine("Handle!"); } |
这不会编译的原因很明显:"$exception"对象的类型和值是什么(存储在变量"e"中)?我们希望编译器处理这一点的方法是注意,两个异常的公共基类型都是"exception",对于一个变量,使用它来包含这两个异常,然后只处理捕获的两个异常。在IL中实现的方法是"filter",它在vb.net中可用。
为了使它在C中工作,我们需要一个具有正确"exception"基类型的临时变量。为了控制代码流,我们可以添加一些分支。下面是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | Exception ex; try { throw new ArgumentException(); // for demo purposes; won't be caught. goto noCatch; } catch (ArgumentOutOfRangeException e) { ex = e; } catch (IndexOutOfRangeException e) { ex = e; } Console.WriteLine("Handle the exception 'ex' here :-)"); // throw ex ? noCatch: Console.WriteLine("We're done with the exception handling."); |
这样做的明显缺点是我们不能正确地扔球,而且-好吧,老实说-这是一个非常丑陋的解决方案。通过执行分支消除,可以稍微解决问题,这使得解决方案稍微更好:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Exception ex = null; try { throw new ArgumentException(); } catch (ArgumentOutOfRangeException e) { ex = e; } catch (IndexOutOfRangeException e) { ex = e; } if (ex != null) { Console.WriteLine("Handle the exception here :-)"); } |
只剩下"回击"。要使其工作,我们需要能够在"catch"块内执行处理,而使其工作的唯一方法是捕获"exception"对象。
此时,我们可以添加一个单独的函数,使用重载解析来处理不同类型的异常,或者处理异常。两者都有缺点。首先,下面是使用helper函数执行此操作的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | private static bool Handle(Exception e) { Console.WriteLine("Handle the exception here :-)"); return true; // false will re-throw; } public static void Main() { try { throw new OutOfMemoryException(); } catch (ArgumentException e) { if (!Handle(e)) { throw; } } catch (IndexOutOfRangeException e) { if (!Handle(e)) { throw; } } Console.WriteLine("We're done with the exception handling."); |
另一种解决方案是捕获异常对象并相应地处理它。基于以上上下文,最直译的是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | try { throw new ArgumentException(); } catch (Exception e) { Exception ex = (Exception)(e as ArgumentException) ?? (e as IndexOutOfRangeException); if (ex != null) { Console.WriteLine("Handle the exception here :-)"); // throw ? } else { throw; } } |
因此得出结论:
- 如果我们不想重新抛出,我们可以考虑捕获正确的异常,并将它们存储在临时的异常中。
- 如果处理程序很简单,并且我们希望重用代码,那么最好的解决方案可能是引入一个助手函数。
- 如果我们想重新抛出代码,我们别无选择,只能将代码放入"exception"捕获处理程序中,这将破坏fxcop和调试器未捕获的异常。
所以你在每个异常切换中重复了很多代码?听起来提取方法是上帝的主意,不是吗?
所以你的代码可以归结为:
1 2 3 4 5 6 7 | MyClass instance; try { instance = ... } catch(Exception1 e) { Reset(instance); } catch(Exception2 e) { Reset(instance); } catch(Exception) { throw; } void Reset(MyClass instance) { /* reset the state of the instance */ } |
我想知道为什么没有人注意到代码重复。
在C 6中,您还有其他人已经提到的异常过滤器。因此,您可以将上面的代码修改为:
这是每个开发人员最终面临的一个经典问题。
让我把你的问题分成两个问题。第一,
我可以一次捕获多个异常吗?
简而言之,没有。
这就引出了下一个问题,
如果不能在同一catch()块中捕获多个异常类型,如何避免编写重复的代码?
考虑到您的具体示例,其中回撤值的构建成本较低,我希望遵循以下步骤:
所以代码看起来像:
1 2 3 4 5 6 7 8 9 10 | try { WebId = Guid.Empty; Guid newGuid = new Guid(queryString["web"]); // More initialization code goes here like // newGuid.x = y; WebId = newGuid; } catch (FormatException) {} catch (OverflowException) {} |
如果抛出任何异常,则WebID永远不会设置为半构造值,并保持guid.empty。
如果构建回退值很昂贵,并且重置值更便宜,那么我会将重置代码移动到它自己的函数中:
1 2 3 4 5 6 7 8 9 10 11 | try { WebId = new Guid(queryString["web"]); // More initialization code goes here. } catch (FormatException) { Reset(WebId); } catch (OverflowException) { Reset(WebId); } |
想把我的简短回答加在这个已经很长的线索上。还没有提到的是catch语句的优先级顺序,更具体地说,您需要了解您试图捕获的每种类型的异常的范围。
例如,如果您使用"catch all"异常作为异常,它将处理所有其他catch语句,并且您显然会得到编译器错误。但是,如果您颠倒了可以链接catch语句的顺序(我认为这是一个反模式),您可以将catch all异常类型放在底部,这将捕获任何异常tha在你的尝试中没有迎合更高的要求。接住障碍:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | try { // do some work here } catch (WebException ex) { // catch a web excpetion } catch (ArgumentException ex) { // do some stuff } catch (Exception ex) { // you should really surface your errors but this is for example only throw new Exception("An error occurred:" + ex.Message); } |
我强烈建议大家查看此msdn文档:
异常层次结构
也许可以尝试使代码保持简单,例如将公共代码放入方法中,就像在不在catch子句中的代码的任何其他部分中那样?
例如。:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | try { // ... } catch (FormatException) { DoSomething(); } catch (OverflowException) { DoSomething(); } // ... private void DoSomething() { // ... } |
我就是这么做的,试着找到简单漂亮的图案
请注意,我确实找到了一种方法,但这看起来更像是日常WTF的材料:
1 2 3 4 5 6 7 8 9 10 11 12 | catch (Exception ex) { switch (ex.GetType().Name) { case"System.FormatException": case"System.OverflowException": WebId = Guid.Empty; break; default: throw; } } |
这里值得一提。您可以响应多个组合(exception error和exception.message)。
我在试图在数据报中强制转换控制对象时遇到了一个用例场景,内容可以是文本框、文本块或复选框。在这种情况下,返回的异常是相同的,但消息不同。
1 2 3 4 5 6 7 8 9 10 11 12 | try { //do something } catch (Exception ex) when (ex.Message.Equals("the_error_message1_here")) { //do whatever you like } catch (Exception ex) when (ex.Message.Equals("the_error_message2_here")) { //do whatever you like } |
打两次"试抓"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
就这么简单!!
在C 6.0中,异常过滤器是对异常处理的改进。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | try { DoSomeHttpRequest(); } catch (System.Web.HttpException e) { switch (e.GetHttpCode()) { case 400: WriteLine("Bad Request"); case 500: WriteLine("Internal Server Error"); default: WriteLine("Generic Error"); } } |