dimanche 4 mai 2014

c# - attraper plusieurs Exceptions à la fois ? -Débordement de pile


It is discouraged to simply catch System.Exception, instead only the "known" Exceptions should be caught.


Now, this sometimes leads to unneccessary repetetive code, for example:


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

I wonder: Is there a way to catch both Exceptions and only call the WebId = Guid.Empty call once?


Edit: the given example is rather simple, as it's only a Guid. But imagine Code where you modify an object multiple times, and if one of the manipulations fail in an expected way, you want to "reset" the object. However, if there is an unexpected Exception, I still want to throw that higher.


About the Answer: Thanks everyone! For some reason, I had my mind set on a switch-case statement which does not support switching on GetType(). Now, there were two answers, one using "typeof" and one using "is". I first thought typeof() would be my Function because I thought "Hey, I only want to catch FormatException because that's the only thing I expect". But that's not how catch() works: catch also catches all derived exceptions. After thinking about it, this is really obvious: Otherwise, catch(Exception ex) would not work! So the correct answer is "is". Yay, learned two things with only one question \o/




Catch System.Exception and switch on the types


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

throw;
}



Not in C# unfortunately, as you'd need an exception filter to do it and C# doesn't expose that feature of MSIL. VB.NET does have this capability though, e.g.


Catch ex As Exception When TypeOf ex Is FormatException OrElse TypeOf ex Is OverflowException

What you could do is use an anonymous function to encapsulate your on-error code, and then call it in those specific catch blocks:


Action onError = () => WebId = Guid.Empty;
try
{
// something
}
catch (FormatException)
{
onError();
}
catch (OverflowException)
{
onError();
}



For the sake of completeness, since .NET 4.0 the code can rewritten as:


Guid.TryParse(queryString["web"], out WebId);

TryParse never throws exceptions and returns false if format is wrong, setting WebId to Guid.Empty.




@Micheal


Slightly revised version of your code:


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

String comparisons are ugly and slow.




The accepted answer seems acceptable, except that CodeAnalysis/FxCop will complain about the fact that it's catching a general exception type.


Also, it seems the "is" operator might degrade performance slightly. http://msdn.microsoft.com/en-us/library/ms182271.aspx says to "consider testing the result of the 'as' operator instead", but if you do that, you'll be writing more code than if you catch each exception separately.


Anyhow, here's what I would do:


bool exThrown = false;

try
{
// something
}
catch( FormatException ){ exThrown = true; }
catch( OverflowException ){ exThrown = true; }

if( exThrown )
{
// something else
}



This is a variant of Matt's answer (I feel that this is a bit cleaner)...use a method:


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

WebId = Guid.Empty;
}

Any other exceptions will be thrown and the code WebId = Guid.Empty; won't be hit. If you don't want other exceptions to crash your program, just add this AFTER the other two catches:


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



I know I'm a little late to the party here, but holy smoke...


Cutting straight to the chase, this kind of duplicates an earlier answer, but if you really want to perform a common action for several exception types and keep the whole thing neat and tidy within the scope of the one method, why not just use a lambda/closure/inline function do something like the following? I mean, chances are pretty good that you'll end up realizing that you just want to make that closure a separate method that you can utilize all over the place. But then it will be super easy to do that without actually changing the rest of the code structurally. Right?


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

I can't help but wonder (warning: a little irony/sarcasm ahead) why on earth go to all this effort to basically just replace the following:


try
{
// try some stuff
}
catch( FormatException ex ){}
catch( OverflowException ex ){}
catch( ArgumentNullException ex ){}

...with some crazy variation of this next code smell, I mean example, only to pretend that you're saving a few keystrokes.


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

Because it certainly isn't automatically more readable.


Granted, I left the three identical instances of /* write to a log, whatever... */ return; out of the first example.


But that's sort of my point. Y'all have heard of functions/methods, right? Seriously. Write a common ErrorHandler function and, like, call it from each catch block.


If you ask me, the second example (with the if and is keywords) is both significantly less readable, and simultaneously significantly more error-prone during the maintenance phase of your project.


The maintenance phase, for anyone who might be relatively new to programming, is going to comprise 98.7% or more of the overall lifetime of your project, and the poor schmuck doing the maintenance is almost certainly going to be someone other than you. And there is a very good chance they will spend 50% of their time on the job cursing your name.


And of course FxCop barks at you and so you have to also add an attribute to your code that has precisely zip to do with the running program, and is only there to tell FxCop to ignore an issue that in 99.9% of cases it is totally correct in flagging. And, sorry, I might be mistaken, but doesn't that "ignore" attribute end up actually compiled into your app?


Would putting the entire if test on one line make it more readable? I don't think so. I mean, I did have another programmer vehemently argue once long ago that putting more code on one line would make it "run faster." But of course he was stark raving nuts. Trying to explain to him (with a straight face--which was challenging) how the interpreter or compiler would break that long line apart into discrete one-instruction-per-line statements--essentially identical to the result if he had gone ahead and just made the code readable instead of trying to out-clever the compiler--had no effect on him whatsoever. But I digress.


How much less readable does this get when you add three more exception types, a month or two from now? (Answer: it gets a lot less readable).


One of the major points, really, is that most of the point of formatting the textual source code that we're all looking at every day is to make it really, really obvious to other human beings what is actually happening when the code runs. Because the compiler turns the source code into something totally different and couldn't care less about your code formatting style. So all-on-one-line totally sucks, too.


Just saying...


// super sucks...
catch( Exception ex )
{
if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
{
// write to a log, whatever...
return;
}
throw;
}



        catch (Exception ex)
{
if (!(
ex is FormatException ||
ex is OverflowException))
{
throw;
}

Console.WriteLine("Hello");
}



How about


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



Geared for people that want a more elegant solution to catch once and filter exceptions, I use an extension method as demonstrated below.


I already had this extension in my library, originally written for other purposes, but it worked just perfectly for type checking on exceptions. Plus, imho, it looks cleaner than a bunch of || statements. Also, unlike the accepted answer, I prefer explicit exception handling so ex is ... had undesireable behaviour as derrived classes are assignable to there parent types).


Usage


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

IsAnyOf.cs Extension (See Full Error Handling Example for Dependancies)


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



Cautioned and Warned: Yet another kind, functional style.


What is in the link doesn't answer your question directly, but it's trivial to extend it to look like:


static void Main() 
{
Action body = () => { ...your code... };

body.Catch<InvalidOperationException>()
.Catch<BadCodeException>()
.Catch<AnotherException>(ex => { ...handler... })();
}

(Basically provide another empty Catch overload which returns itself)


The bigger question to this is why. I do not think the cost outweighs the gain here :)




As others have pointed out, you can have an if statement inside your catch block to determine what is going on. Announced at Build 2014, the next version will support Exception Filters, so the following will work:


try { … }
catch (MyException e) if (myfilter(e))
{

}

You can download the preview of Roslyn now to check this out.




A little mind twister, probably not for real life use, posting here just for the sake of having another option and seeing how ugly it is


   catch (Exception ex)            
{
if (ex is FormatException ||
ex is OverflowException)
{} else throw;

WebId = Guid.Empty;
}



Note that I did find one way to do it, but this looks more like Material for TheDailyWTF:


            catch (Exception ex)
{

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



Design an exception inheritance hierarchy for your application.



It is discouraged to simply catch System.Exception, instead only the "known" Exceptions should be caught.


Now, this sometimes leads to unneccessary repetetive code, for example:


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

I wonder: Is there a way to catch both Exceptions and only call the WebId = Guid.Empty call once?


Edit: the given example is rather simple, as it's only a Guid. But imagine Code where you modify an object multiple times, and if one of the manipulations fail in an expected way, you want to "reset" the object. However, if there is an unexpected Exception, I still want to throw that higher.


About the Answer: Thanks everyone! For some reason, I had my mind set on a switch-case statement which does not support switching on GetType(). Now, there were two answers, one using "typeof" and one using "is". I first thought typeof() would be my Function because I thought "Hey, I only want to catch FormatException because that's the only thing I expect". But that's not how catch() works: catch also catches all derived exceptions. After thinking about it, this is really obvious: Otherwise, catch(Exception ex) would not work! So the correct answer is "is". Yay, learned two things with only one question \o/



Catch System.Exception and switch on the types


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

throw;
}


Not in C# unfortunately, as you'd need an exception filter to do it and C# doesn't expose that feature of MSIL. VB.NET does have this capability though, e.g.


Catch ex As Exception When TypeOf ex Is FormatException OrElse TypeOf ex Is OverflowException

What you could do is use an anonymous function to encapsulate your on-error code, and then call it in those specific catch blocks:


Action onError = () => WebId = Guid.Empty;
try
{
// something
}
catch (FormatException)
{
onError();
}
catch (OverflowException)
{
onError();
}


For the sake of completeness, since .NET 4.0 the code can rewritten as:


Guid.TryParse(queryString["web"], out WebId);

TryParse never throws exceptions and returns false if format is wrong, setting WebId to Guid.Empty.



@Micheal


Slightly revised version of your code:


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

String comparisons are ugly and slow.



The accepted answer seems acceptable, except that CodeAnalysis/FxCop will complain about the fact that it's catching a general exception type.


Also, it seems the "is" operator might degrade performance slightly. http://msdn.microsoft.com/en-us/library/ms182271.aspx says to "consider testing the result of the 'as' operator instead", but if you do that, you'll be writing more code than if you catch each exception separately.


Anyhow, here's what I would do:


bool exThrown = false;

try
{
// something
}
catch( FormatException ){ exThrown = true; }
catch( OverflowException ){ exThrown = true; }

if( exThrown )
{
// something else
}


This is a variant of Matt's answer (I feel that this is a bit cleaner)...use a method:


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

WebId = Guid.Empty;
}

Any other exceptions will be thrown and the code WebId = Guid.Empty; won't be hit. If you don't want other exceptions to crash your program, just add this AFTER the other two catches:


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


I know I'm a little late to the party here, but holy smoke...


Cutting straight to the chase, this kind of duplicates an earlier answer, but if you really want to perform a common action for several exception types and keep the whole thing neat and tidy within the scope of the one method, why not just use a lambda/closure/inline function do something like the following? I mean, chances are pretty good that you'll end up realizing that you just want to make that closure a separate method that you can utilize all over the place. But then it will be super easy to do that without actually changing the rest of the code structurally. Right?


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

I can't help but wonder (warning: a little irony/sarcasm ahead) why on earth go to all this effort to basically just replace the following:


try
{
// try some stuff
}
catch( FormatException ex ){}
catch( OverflowException ex ){}
catch( ArgumentNullException ex ){}

...with some crazy variation of this next code smell, I mean example, only to pretend that you're saving a few keystrokes.


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

Because it certainly isn't automatically more readable.


Granted, I left the three identical instances of /* write to a log, whatever... */ return; out of the first example.


But that's sort of my point. Y'all have heard of functions/methods, right? Seriously. Write a common ErrorHandler function and, like, call it from each catch block.


If you ask me, the second example (with the if and is keywords) is both significantly less readable, and simultaneously significantly more error-prone during the maintenance phase of your project.


The maintenance phase, for anyone who might be relatively new to programming, is going to comprise 98.7% or more of the overall lifetime of your project, and the poor schmuck doing the maintenance is almost certainly going to be someone other than you. And there is a very good chance they will spend 50% of their time on the job cursing your name.


And of course FxCop barks at you and so you have to also add an attribute to your code that has precisely zip to do with the running program, and is only there to tell FxCop to ignore an issue that in 99.9% of cases it is totally correct in flagging. And, sorry, I might be mistaken, but doesn't that "ignore" attribute end up actually compiled into your app?


Would putting the entire if test on one line make it more readable? I don't think so. I mean, I did have another programmer vehemently argue once long ago that putting more code on one line would make it "run faster." But of course he was stark raving nuts. Trying to explain to him (with a straight face--which was challenging) how the interpreter or compiler would break that long line apart into discrete one-instruction-per-line statements--essentially identical to the result if he had gone ahead and just made the code readable instead of trying to out-clever the compiler--had no effect on him whatsoever. But I digress.


How much less readable does this get when you add three more exception types, a month or two from now? (Answer: it gets a lot less readable).


One of the major points, really, is that most of the point of formatting the textual source code that we're all looking at every day is to make it really, really obvious to other human beings what is actually happening when the code runs. Because the compiler turns the source code into something totally different and couldn't care less about your code formatting style. So all-on-one-line totally sucks, too.


Just saying...


// super sucks...
catch( Exception ex )
{
if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
{
// write to a log, whatever...
return;
}
throw;
}


        catch (Exception ex)
{
if (!(
ex is FormatException ||
ex is OverflowException))
{
throw;
}

Console.WriteLine("Hello");
}


How about


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


Geared for people that want a more elegant solution to catch once and filter exceptions, I use an extension method as demonstrated below.


I already had this extension in my library, originally written for other purposes, but it worked just perfectly for type checking on exceptions. Plus, imho, it looks cleaner than a bunch of || statements. Also, unlike the accepted answer, I prefer explicit exception handling so ex is ... had undesireable behaviour as derrived classes are assignable to there parent types).


Usage


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

IsAnyOf.cs Extension (See Full Error Handling Example for Dependancies)


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


Cautioned and Warned: Yet another kind, functional style.


What is in the link doesn't answer your question directly, but it's trivial to extend it to look like:


static void Main() 
{
Action body = () => { ...your code... };

body.Catch<InvalidOperationException>()
.Catch<BadCodeException>()
.Catch<AnotherException>(ex => { ...handler... })();
}

(Basically provide another empty Catch overload which returns itself)


The bigger question to this is why. I do not think the cost outweighs the gain here :)



As others have pointed out, you can have an if statement inside your catch block to determine what is going on. Announced at Build 2014, the next version will support Exception Filters, so the following will work:


try { … }
catch (MyException e) if (myfilter(e))
{

}

You can download the preview of Roslyn now to check this out.



A little mind twister, probably not for real life use, posting here just for the sake of having another option and seeing how ugly it is


   catch (Exception ex)            
{
if (ex is FormatException ||
ex is OverflowException)
{} else throw;

WebId = Guid.Empty;
}


Note that I did find one way to do it, but this looks more like Material for TheDailyWTF:


            catch (Exception ex)
{

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


Design an exception inheritance hierarchy for your application.


0 commentaires:

Enregistrer un commentaire