一次捕获多个异常?

只是简单地捕获System.Exception就不鼓励了。 相反,只有“已知的”例外应该被捕获。

现在,这有时会导致不必要的重复代码,例如:

 try { WebId = new Guid(queryString["web"]); } catch (FormatException) { WebId = Guid.Empty; } catch (OverflowException) { WebId = Guid.Empty; } 

我想知道:是否有一种方法来捕获这两个异常,只调用WebId = Guid.Empty调用一次?

给出的例子很简单,因为它只是一个GUID 。 但想象一下代码在多次修改对象的位置,如果其中一个操作以预期的方式失败,则需要“重置”该object 。 但是,如果出现意想不到的例外情况,我还是要把这个提高一些。

捕获System.Exception并打开类型

 catch (Exception ex) { if (ex is FormatException || ex is OverflowException) { WebId = Guid.Empty; return; } throw; } 

编辑:我也赞同其他人说,从C#6.0,异常过滤器现在是一个完美的方式去: catch (Exception ex) when (ex is ... || ex is ... )

除此之外,我还是很讨厌这种一字排开的版本,并且会像下面那样亲自把代码放在外面。 我认为这是审美功能,因为我相信它提高了理解。 有些人可能不同意:

 catch (Exception ex) when ( ex is ... || ex is ... || ex is ... ) 

原版的:

我知道我在这里的晚会有点晚,但是圣洁的烟雾…

直截了当地进行追踪,这种类型复制了一个较早的答案,但如果你真的想为几个异常类型执行一个共同的操作,并保持整个事物在一个方法的范围内整齐,为什么不使用一个lambda /闭合/内联函数做如下的事情? 我的意思是,机会是相当好的,你会最终意识到,你只是想使这个封闭的方法,你可以利用各地。 但是,如果不在结构上实际改变代码的其余部分,将会非常容易。 对?

 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;的三个相同的实例/* write to a log, whatever... */ return; 出了第一个例子。

但是,这是我的观点。 你们都听说过功能/方法吧? 认真。 编写一个通用的ErrorHandler函数,就像从每个catch块中调用它。

如果你问我,第二个例子(使用ifis关键字)的可读性明显降低,同时在项目的维护阶段同时出现更多的错误。

对于那些可能相对比较陌生的人来说,维护阶段将占项目整个生命周期的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; } 

正如其他人所指出的,你可以在你的catch块中有一个if语句来确定发生了什么。 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) { … } 

这与在catch块中使用if语句不同,使用异常过滤器不会展开堆栈。

您可以下载Visual Studio 2015来检查这一点。

如果您想继续使用Visual Studio 2013,则可以安装以下nuget包:

安装包Microsoft.Net.Compilers

在撰写本文时,这将包括对C#6的支持。

引用此包将导致项目使用包中包含的特定版本的C#和Visual Basic编译器进行构建,而不是任何系统安装版本。

不幸的是,在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(); } 

为了完整性,从.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; 

接下来无用的更新,这个无用的答案来解构的out参数是在C#12中实现的。

如果您可以将您的应用程序升级到C#6,那么您很幸运。 新的C#版本已经实现了异常过滤器。 所以你可以写这个:

 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上查看关于此的讨论 。 并举例说明差异 。

如果您不想在catch范围内使用if语句,那么C# 6.0 ,可以使用 CLR在预览版本中已经支持但仅在VB.NET / MSIL存在的Exception Filters语法

 try { WebId = new Guid(queryString["web"]); } catch (Exception exception) when (exception is FormatException || ex is OverflowException) { WebId = Guid.Empty; } 

只有在出现InvalidDataExceptionArgumentNullException时,此代码才会捕获Exception

其实,你可以基本上把条件里面的任何条件都放进去:

 static int a = 8; ... catch (Exception exception) when (exception is InvalidDataException && a == 8) { Console.WriteLine("Catch"); } 

请注意,与catch范围内的if语句相反, 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语句,而是被FiltersExceptions不能从标有Filter 1Filter 2的区域中抛出,但是抛出Exception的过滤器将会失败,而最后在endfilter命令之前将比较值压入堆栈将决定过滤器的成功/失败( Catch 1 XOR Catch 2将相应执行):

异常过滤器MSIL

另外,具体GuidGuid.TryParse方法。

这是马特的答案的变种(我觉得这是一个更清洁)…使用一种方法:

 public void TryCatch(...) { try { // something return; } catch (FormatException) {} catch (OverflowException) {} WebId = Guid.Empty; } 

任何其他异常将被抛出,代码WebId = Guid.Empty; 将不会被击中。 如果你不想让其他的异常崩溃你的程序,只需在后面添加另外两个catch:

 ... catch (Exception) { // something, if anything return; // only need this if you follow the example I gave and put it all in a method } 

被接受的答案似乎是可以接受的,只不过CodeAnalysis / FxCop会抱怨它正在捕获一般的异常类型。

另外,似乎“是”运营商可能会略微降低性能。

CA1800:不要不必要地说“考虑测试'运算符的结果',而是如果你这样做,你会写更多的代码,而不是单独捕获每个异常。

无论如何,这是我会做的:

 bool exThrown = false; try { // Something } catch (FormatException) { exThrown = true; } catch (OverflowException) { exThrown = true; } if (exThrown) { // Something else } 

在C#6中推荐的方法是使用Exception Filters,这里是一个例子:

  try { throw new OverflowException(); } catch(Exception e ) when ((e is DivideByZeroException) || (e is OverflowException)) { // this will execute iff e is DividedByZeroEx or OverflowEx Console.WriteLine("E"); } 

@Micheal

稍微修改你的代码的版本:

 catch (Exception ex) { Type exType = ex.GetType(); if (exType == typeof(System.FormatException) || exType == typeof(System.OverflowException) { WebId = Guid.Empty; } else { throw; } } 

字符串比较是丑陋和缓慢的。

约瑟夫戴格尔的答案是一个很好的解决方案,但我发现以下结构有点整齐,并且不太容易出错。

 catch(Exception ex) { if (!(ex is SomeException || ex is OtherException)) throw; // Handle exception } 

反转表达式有一些优点:

  • 返回声明是没有必要的
  • 代码不嵌套
  • 不要忘记Joseph的解决方案中的“抛出”或“返回”表述与表达式分离的风险。

它甚至可以压缩到一个单一的线(虽然不是很漂亮)

 catch(Exception ex) { if (!(ex is SomeException || ex is OtherException)) throw; // Handle exception } 

编辑:在C#6.0中的异常过滤将使语法更清洁一些,并带来许多其他好处超过任何当前的解决方案。 (最值得注意的就是没有受到伤害)

下面是使用C#6.0语法的同样的问题:

 catch(Exception ex) when (ex is SomeException || ex is OtherException) { // Handle exception } 

警告和警告: 又一种功能风格。

链接中的内容并不直接回答你的问题,而是将其扩展为如下所示:

 static void Main() { Action body = () => { ...your code... }; body.Catch<InvalidOperationException>() .Catch<BadCodeException>() .Catch<AnotherException>(ex => { ...handler... })(); } 

(基本上提供另一个空的Catch超载,它自己返回)

更大的问题是为什么 。 我不认为成本超过这里的收益:)

 catch (Exception ex) { if (!( ex is FormatException || ex is OverflowException)) { throw; } Console.WriteLine("Hello"); } 

怎么样

 try { WebId = Guid.Empty; WebId = new Guid(queryString["web"]); } catch (FormatException) { } catch (OverflowException) { } 

更新2015-12-15:请参阅https://stackoverflow.com/a/22864936/1718702 C#6。 这是一个更清洁,现在的标准语言。

面向那些想要更优雅的解决方案来捕捉一次并过滤异常的人们,我使用如下所示的扩展方法。

我在我的库中已经有了这个扩展,最初是为了其他目的而编写的,但它对于异常type检查非常合适。 另外,imho,它看起来比一堆||更干净 声明。 另外,与接受的答案不同,我更喜欢明确的异常处理,因此ex is ...具有不希望的行为,因为可以将父类型指派给父类型)。

用法

 if (ex.GetType().IsAnyOf( typeof(FormatException), typeof(ArgumentException))) { // Handle } else throw; 

IsAnyOf.cs扩展(请参阅完全错误处理示例的依赖)

 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; } } } 

Full Error Handling Example (Copy-Paste to new Console app)

 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.\r\nParameter_Name: " + p_name, default(Exception)); if (p_parameter.Count <= 0) throw new ArgumentOutOfRangeException("Collection cannot be empty.\r\nParameter_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.\r\nParameter_Name: " + p_name, default(Exception)); } } } 

Two Sample NUnit Unit Tests

Matching behaviour for Exception types is exact (ie. A child IS NOT a match for any of its parent types).

 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*/ } } } } 

Since I felt like these answers just touched the surface, I attempted to dig a bit deeper.

So what we would really want to do is something that doesn't compile, say:

 // Won't compile... damn public static void Main() { try { throw new ArgumentOutOfRangeException(); } catch (ArgumentOutOfRangeException) catch (IndexOutOfRangeException) { // ... handle } 

The reason we want this is because we don't want the exception handler to catch things that we need later on in the process. Sure, we can catch an Exception and check with an 'if' what to do, but let's be honest, we don't really want that. (FxCop, debugger issues, uglyness)

So why won't this code compile – and how can we hack it in such a way that it will?

If we look at the code, what we really would like to do is forward the call. However, according to the MS Partition II, IL exception handler blocks won't work like this, which in this case makes sense because that would imply that the 'exception' object can have different types.

Or to write it in code, we ask the compiler to do something like this (well it's not entirely correct, but it's the closest possible thing I guess):

 // Won't compile... damn try { throw new ArgumentOutOfRangeException(); } catch (ArgumentOutOfRangeException e) { goto theOtherHandler; } catch (IndexOutOfRangeException e) { theOtherHandler: Console.WriteLine("Handle!"); } 

The reason that this won't compile is quite obvious: what type and value would the '$exception' object have (which are here stored in the variables 'e')? The way we want the compiler to handle this is to note that the common base type of both exceptions is 'Exception', use that for a variable to contain both exceptions, and then handle only the two exceptions that are caught. The way this is implemented in IL is as 'filter', which is available in VB.Net.

To make it work in C#, we need a temporary variable with the correct 'Exception' base type. To control the flow of the code, we can add some branches. 开始:

  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."); 

The obvious disadvantages for this are that we cannot re-throw properly, and -well let's be honest- that it's quite the ugly solution. The uglyness can be fixed a bit by performing branch elimination, which makes the solution slightly better:

 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 :-)"); } 

That leaves just the 're-throw'. For this to work, we need to be able to perform the handling inside the 'catch' block – and the only way to make this work is by an catching 'Exception' object.

At this point, we can add a separate function that handles the different types of Exceptions using overload resolution, or to handle the Exception. Both have disadvantages. To start, here's the way to do it with a helper function:

 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."); 

And the other solution is to catch the Exception object and handle it accordingly. The most literal translation for this, based on the context above is this:

 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; } } 

So to conclude:

  • If we don't want to re-throw, we might consider catching the right exceptions, and storing them in a temporary.
  • If the handler is simple, and we want to re-use code, the best solution is probably to introduce a helper function.
  • If we want to re-throw, we have no choice but to put the code in a 'Exception' catch handler, which will break FxCop and your debugger's uncaught exceptions.

So you´re repeating lots of code within every exception-switch? Sounds like extracting a method would be god idea, doesn´t it?

So your code comes down to this:

 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 */ } 

I wonder why no-one noticed that code-duplication.

From C#6 you furthermore have the exception-filters as already mentioned by others. So you can modify the code above to this:

 try { ... } catch(Exception e) when(e is Exception1 || e is Exception2) { Reset(instance); } 

Note that I did find one way to do it, but this looks more like material for The Daily WTF :

 catch (Exception ex) { switch (ex.GetType().Name) { case "System.FormatException": case "System.OverflowException": WebId = Guid.Empty; break; default: throw; } } 

Wanted to added my short answer to this already long thread. Something that hasn't been mentioned is the order of precedence of the catch statements, more specifically you need to be aware of the scope of each type of exception you are trying to catch.

For example if you use a "catch-all" exception as Exception it will preceed all other catch statements and you will obviously get compiler errors however if you reverse the order you can chain up your catch statements (bit of an anti-pattern I think) you can put the catch-all Exception type at the bottom and this will be capture any exceptions that didn't cater for higher up in your try..catch block:

  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); } 

I highly recommend folks review this MSDN document:

Exception Hierarchy

 catch(ArithmeticException | ArrayIndexOutOfBounds | RunTimeException | Exception ex) { ex.printStackTrace(); } 

You have to put More Specific Exceptions..ie Subclasses first and then superclasses…

Just call the try and catch twice.

 try { WebId = new Guid(queryString["web"]); } catch (FormatException) { WebId = Guid.Empty; } try { WebId = new Guid(queryString["web"]); } catch (OverflowException) { WebId = Guid.Empty; } 

It is just that Simple!!

In c# 6.0,Exception Filters is improvements for exception handling

 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"); } }