다음을 통해 공유


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

Comments

  • Anonymous
    May 07, 2007
    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)            {              exceptionsexceptionType;              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), () => { } } }));

  • Anonymous
    May 09, 2007
    The only problem being edit and continue doesn't work within anonymous methods.

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

  • Anonymous
    May 13, 2007
    Welcome to the 27th Community Convergence. I use this column to keep you informed of events in the C#

  • Anonymous
    May 22, 2007
    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);            }        }    } }

  • Anonymous
    November 19, 2007
    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.

  • Anonymous
    July 09, 2008
    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();            }

  • Anonymous
    July 14, 2008
    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(...);  }); } ...

  • Anonymous
    January 02, 2011
    Wow! You have managed to transform a very helpful thought into reality. I was looking for someone who could help me in managing my assets. And your site has everything I needed for my asset protection. Not only your site but also your marketing team is very helpful to me. I thank you for that. <a href="http://www.byebyebigbrother. org">anonymous transactions  </a>

  • Anonymous
    February 02, 2011
    Hey! Thanks for your company’s instant help. Otherwise I would have lost my assets and wealth. I liked your solutions very much. Your company’s employees are so professional that they heard my problems and gave me solutions instantly. They also taught me about tax avoidance. I refer others to try this website to solve their problems in a satisfactory way. Thanks again. <a href ="http://www.byebyebigbrother.com">tax avoidance </a>