Welcome to MSDN Blogs Sign in | Join | Help

Mitsu's blog

Discussing topics related to .Net, WPF, C# and Linq
Anonymous methods: how to factorize surrounding statements like try-catch

 

Here is the translation of one of my old french articles at a moment when I was trying to find what I could do with anonymous methods :p !

Here is the idea:

Let's imagine we would like to centralize exception management. In numerous program (like windows API), a classical solution consist in implementing methods returning an integer. It's quite easy to define a rule like: -1 equals "no exception" and 0 and greater are representing the error number (or code).

Then it becomes easy to define a ShowError(int errorCode) method to centralize error management. Using this pattern, we still have to write try-catch statements every time because they just can not be factorized.

 

private int AddCustomer(string param) { try { int i = Convert.ToInt32(param); } catch { return 1; } return 0; } private void ShowError(int value) { if (value != 0) switch (value) { case 1: //error 1 case 2: //error 2 default: MessageBox.Show("Error has been raised:" + value.ToString()); break; } }

The call now looks like:

 

ShowError(AddCustomer("blabla"));

The idea is to use C# 2.0 anonymous methods to delay critical code execution. Then we have time to surround the delegate referencing the anonymous method with any kind of surrounding statement like try-catch. We will use the simplest delegate as possible (void()). Let's remind that in an anonymous method, we can access to the same scope elements than the hosting method, so having no parameter in our delegate is not a problem.

 

public delegate void SimpleDelegate();

Then we can implement a unique method for exception handling including our try-catch statement.

 

private void SafeCall(SimpleDelegate d) { try { if (d != null) d(); } catch (Exception e) { MessageBox.Show("Error has been raised:" + e.Message); } }

The call becomes really simpler:

string s = "blabla"; SafeCall(delegate { int i = Convert.ToInt32(s); });

We can imagine using this same pattern to factorize transactions:

 

InTransaction(delegate { AddCustomer(); });
Posted: Monday, May 07, 2007 11:07 PM by mitsu
Filed under: , ,

Comments

Matthieu MEZIL said:

For frenches, you can find this in a very interesting Mitsu's webcast : http://msdn2.microsoft.com/fr-fr/events/bb400908.aspx.

I have use your code like this:

public static class ExceptionSafe

{

 public delegate void SimpleDelegate();

 public enum Result

 {

   Ok,

   ExceptionCatched,

   Exception

 }

 public static Result SafeCall(SimpleDelegate d, Dictionary<Type, SimpleDelegate> exceptions)

 {

   if (d != null)

     try

     {

       d();

     }

     catch (Exception e)

     {

       if (exceptions == null)

         return Result.Exception;

       // We try to find the more typed exeption

       Type exceptionType = e.GetType();

       while (exceptionType != typeof(Object))

       {

         foreach (Type dictionaryExceptionType in exceptions.Keys)

           if (exceptionType == dictionaryExceptionType)

           {

             exceptions[exceptionType]();

             return Result.ExceptionCatched;

           }

         exceptionType = exceptionType.BaseType;

       }

       return Result.Exception;

     }

   return Result.Ok;

 }

}

But it's painful to initialize exceptions dictionary. So I have some methods with exception type and delegate in parameters.

With C#3.0, with objects initializers, it is not a problem to have only one method with dictionary in parameter.

Ex:

Console.WriteLine(ExceptionSafe.SafeCall(delegate { throw new ArgumentNullException(); }, new Dictionary<Type, ExceptionSafe.SimpleDelegate> { { typeof(ArgumentException), () => { } } }));

# May 8, 2007 5:19 AM

Gabriel Kevorkian said:

The only problem being edit and continue doesn't work within anonymous methods.

# May 10, 2007 3:33 AM

mitsu said:

I personaly never use edit and continue but I did not knew that. Unfortunately, I don't think this will be added in Orcas.

# May 10, 2007 5:27 AM

Charlie Calvert's Community Blog said:

Welcome to the 27th Community Convergence. I use this column to keep you informed of events in the C#

# May 14, 2007 2:19 AM

Ian Gibson said:

I have been playing around with similar ideas, my work colleague pointed me this way so I thought I would share the code I've been messing around with.

The idea is to allow for a common catch routine for many exception types or to catch many exceptions with a small amount of code. Something along the lines of...

ExceptionHandler.Handle<InvalidOperationException, ArgumentNullException>(FunctionName, parameter1, Parameter2);

this nearly works but the compiler cant quite infer all the types itself when used in the above fashion. So the code has to look something like

ExceptionHandler.Handle<InvalidOperationException, ArgumentNullException, string, int>(SomeFunction, "test", 10);

The declaration for handle is as follows

      delegate void UnitFunc<T1, T2>(T1 t1, T2 t2);

      public static void Handle<TE1, TE2, T1, T2>(UnitFunc<T1, T2> function, T1 t1, T2 t2)

           where TE1 : Exception

           where TE2 : Exception

       {

           try

           {

               function(t1, t2);

           }

           catch (TE1)

           {

           }

           catch (TE2)

           {

           }

       }

So the handler takes a delegate with 2 arguments and catches the two exception types in the generic parameter list. I don't know why the compiler has trouble inferring the types from the simple call to the function without specifying the generic types as it can work out the types for the following function call

   static void DoFunction<T1, T2, T3, T4>(UnitFunc<T1, T2, T3, T4> func, T1 t1, T2 t2, T3 t3, T4 t4)

   {

       func(t1, t2, t3, t4);

   }

DoFunction(SomeFunction, "test", 10, new object(), 10.98);

In the example above the generic parameters are not required which makes the syntax a lot nicer.

The idea can be extended by providing a common catching block for a function...

       UnitFunc<Exception> commonCatch = delegate(Exception e)

       {

           Console.WriteLine("caught an exception: " + e.Message);

       };

       ExceptionHandler.Handle<ArgumentNullException, InvalidOperationException, InvalidCastException>(

           delegate() { SomeFunction("test", 0); }, commonCatch);

       public static void Handle<TE1, TE2, TE3>(UnitFunc function, UnitFunc<Exception> handler)

           where TE1 : Exception

           where TE2 : Exception

           where TE3 : Exception

       {

           try

           {

               function();

           }

           catch (TE1 e1)

           {

               handler(e1);

           }

           catch (TE2 e2)

           {

               handler(e2);

           }

           catch (TE3 e3)

           {

               handler(e3);

           }

       }

In this case the exception is passed to the generic handler each time. This is all done with .NET 2 so I'm hoping the .NET 3 compiler might be able to infer the types in the first call.

Full source...

using System;

class Program

{

   delegate void UnitFunc();

   delegate void UnitFunc<T1>(T1 t1);

   delegate void UnitFunc<T1, T2>(T1 t1, T2 t2);

   delegate void UnitFunc<T1, T2, T3, T4>(T1 t1, T2 t2, T3 t3, T4 t4);

   static void Main()

   {

       ExceptionHandler.Handle<InvalidOperationException, ArgumentNullException, string, int>(SomeFunction, "test", 10);

       UnitFunc<Exception> commonCatch = delegate(Exception e)

       {

           Console.WriteLine("caught an exception: " + e.Message);

       };

       ExceptionHandler.Handle<ArgumentNullException, InvalidOperationException, InvalidCastException>(

           delegate() { SomeFunction("test", 0); }, commonCatch);

       DoFunction(SomeFunction, "test", 10, new object(), 10.98);

   }

   static void SomeFunction(string s, int i)

   {

       throw new ArgumentNullException();

   }

   static void SomeFunction(string s, int i, object o, double d)

   {

   }

   static void DoFunction<T1, T2, T3, T4>(UnitFunc<T1, T2, T3, T4> func, T1 t1, T2 t2, T3 t3, T4 t4)

   {

       func(t1, t2, t3, t4);

   }

   static class ExceptionHandler

   {

       public static void Handle<TE1, TE2, T1, T2>(UnitFunc<T1, T2> function, T1 t1, T2 t2)

           where TE1 : Exception

           where TE2 : Exception

       {

           try

           {

               function(t1, t2);

           }

           catch (TE1)

           {

           }

           catch (TE2)

           {

           }

       }

       public static void Handle<TE1, TE2, TE3>(UnitFunc function, UnitFunc<Exception> handler)

           where TE1 : Exception

           where TE2 : Exception

           where TE3 : Exception

       {

           try

           {

               function();

           }

           catch (TE1 e1)

           {

               handler(e1);

           }

           catch (TE2 e2)

           {

               handler(e2);

           }

           catch (TE3 e3)

           {

               handler(e3);

           }

       }

   }

}

# May 22, 2007 2:10 PM

Mike Brown said:

Interesting, I had done something similar a few years ago using anonymous methods and the Command Pattern to perform "pseudo-aspects". Basically, I was tired of writing code for opening database connections, catching exceptions, creating transactions, and all that other fun stuff. So I created a set of classes that handled all of it for me. When I wanted to add new functionality (like user authorization), I only had to do it in one place rather than within each and every business function.

# November 19, 2007 2:39 PM

Diviner said:

How to put the Transaction Block into this modeling? Code like this:

           ctx.Connection.Open();

           try

           {

               System.Data.IsolationLevel readCommitted =

                   System.Data.IsolationLevel.ReadCommitted;

               DbTransaction trn = ctx.Connection.BeginTransaction(readCommitted);

               ctx.Transaction = trn;

               try

               {

                   ctx.ExecuteCommand("insert into test values('test', 0)");

                   ctx.ExecuteCommand("delete from test where name = 'test'");

                   ctx.ExecuteCommand("insert into test values('test', 1)");

               }

               finally

               {

                   trn.Rollback();

               }

           }

           finally

           {

               ctx.Connection.Close();

           }

# July 9, 2008 10:08 PM

mitsu said:

Something like:

public class TransactionHelper

{

 public static void Do(DBConnection cnx, Action action)

 {

   DBTransaction trn = cnx.BeginTransaction();

   try

   {

     action();

     trn.Commit();

   }

   catch()

   {

     trn.Rollback();

   }

 }

}

...

using (DBConnection cnx = new SqlConnection(...))

{

 TransactionHelper.Do(cnx,

 delegate

 {

   ctx.ExecuteCommand(...);

   ctx.ExecuteCommand(...);

   ctx.ExecuteCommand(...);

 });

}

...

# July 14, 2008 9:10 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

  
Enter Code Here: Required

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Page view tracker