timiil
无才可去补单车
posts - 2,  comments - 22,  trackbacks - 0

    Lambda来了很久了,他有许多特性。其中之一,可以说是一种快速撰写匿名委托/函数的语法吧。最近偶然拍脑袋,觉得可以用匿名委托来实现一下
ExceptionHandling,希望能得到轻量,可任意扩展,可重用的效果。所以特别撰写本文,望与诸君一起讨论一下,这种方式的实现究竟有无实际意义。

    首先看看我们平常使用的方式:   

try
    
{
    normalCode();     
//业务代码
    }

    
catch (ExceptionType1 ex1)
    
{
    handleExceptionType1Code(ex1);    
//处理ExceptionType1类型的异常
    }

    
catch (ExceptionType2 ex2)
    
{
    handleExceptionType2Code(ex2);  
//处理ExceptionType2类型的异常
    }

    
    
finally
    
{
    finallyCode();
    }

     这种方式下,我们可以预先撰写一堆异常处理的策略已达到重用效果, 但实际使用上,这样做最大的缺点就是代码冗长不堪,相同的异常策略只能通过复制粘贴来重用,感觉非常石器:(


      我们再看看Exception Handling Application Block的方式:
try 
    { 
         
// Run code. 
    } 
    
catch(Exception ex) 
    {
         
bool rethrow = ExceptionPolicy.HandleException(ex, " Data Access Policy"); 
         
if (rethrow) 
         
throw
    }

     EHAB这种模式,写得可以简单很多, 但让人觉得不爽的是,由于直接先行Catch住Exception,若发现未处理的异常类型,就必需重抛异常。但这个重抛因为已经离开了事发地点,StackTrace等信息都不同了,给我们Debug带来困扰。

    我们现在有了匿名委托,其实要做一个这样的工具类,已经非常简单。首先构建一个简单得很的类如下:

class Try<TException> where TException : Exception
    {
        
public Action<object> FinallyAction { getset; }

        
public Action<TException> Handler { getset; }

        
public Exception Exception { getprivate set; }

        
public bool Execute(Action<object> action)
        {
            
try
            {
                action.Invoke(
null);
            }
            
catch (TException ex)
            {
                
this.Handler(ex);
                
this.Exception = ex;
            }
            
finally
            {
                
if (this.FinallyAction != null)
                    
this.FinallyAction(null);
            }

            
return this.Exception != null;
        }
    }

然后可以简单测试如下:

class Test
    {
        
public static void ABC()
        {
            Try
<NullReferenceException> Try = new Try<NullReferenceException>()
            {
                Handler 
= f => System.Windows.Forms.MessageBox.Show(f.Message),
                FinallyAction 
= f => System.Windows.Forms.MessageBox.Show("FinallyTest")
            };

            var ret 
= Try.Execute(f =>          //test1:
                {
                    
throw new NullReferenceException();
                });
            
if (!ret)
                
return;

            ret 
= Try.Execute(f =>          //test2
            {
                
throw new IndexOutOfRangeException();   //这里直接爆异常,因为并没有Handle这种类型
            });


        }
    }

 

      首个测试中,我们可以看到NullReferenceException成功地被Cacth了,而第二次测试,则原地爆出了异常。

      这个简单的类,基本具有了最基本的Try/Cacth块功能,只是。。。只是还不能支持同时Catch多个ExceptionType,要实现这个Feature,我们有很多办法,随 便想想至少3种:

    1, 写class Try<TException1, TException2>, class Try<TException1, TException2, TException3>...
        只要愿意,写六七十个的版本也可以。

    2, 使用Action,Func进行嵌套调用,即变形为:

 

try
        {
            
try
            {
                     coreWork();    
//核心代码
            }
            
catch (ExceptionType1 ex1)    //抓捕第一异常类型
            {
                 handleExceptionType1(ex1);                     
            }
    }
        cacth (ExceptionType2 ex2)     
//抓捕第二异常类型
        {
        handleExceptionType2(ex2);                         
        }

 

    3, 用Emit做DynamicMethod,这种应该最高效率。

    具体的实现我不在这里啰嗦了,目前是随手用第二种方式实现的。

 

/// <summary>
    
/// 异常捕捉执行块基类
    
/// </summary>

    public abstract class TryBlockBase
    
{
        
/// <summary>
        
/// 异常对象
        
/// </summary>

        public Exception Exception getprotected set; }

        
/// <summary>
        
/// 执行结果有异常
        
/// </summary>

        public bool Faulted
        
{
            
get
            
{
                
return this.Exception != null;
            }

        }


        
/// <summary>
        
/// 执行序列
        
/// </summary>

        protected List<Func<object, Exception>> list = new List<Func<object, Exception>>();

        
/// <summary>
        
/// 工作块
        
/// </summary>

        protected Action<object> work;

        
/// <summary>
        
/// finally执行区块
        
/// </summary>

        protected Action<object> finallyAction;

        
/// <summary>
        
/// 当前执行深度
        
/// </summary>

        protected int depth;

        
/// <summary>
        
/// 添加异常处理区块
        
/// </summary>
        
/// <typeparam name="E">异常泛型类型</typeparam>
        
/// <param name="action">异常处理匿名执行函数</param>
        
/// <returns>返回自身,方便快速设置</returns>

        protected TryBlockBase _catch<E>(Action<E> action) where E : Exception
        
{
            Func
<object, Exception> newFun = f =>
            
{
                
this.depth--;
                
try
                
{
                    
if (depth == -1)
                    
{
                        
this.work(null);
                        
return null;
                    }

                    
else
                    
{
                        
return this.list[depth](null);
                    }

                }

                
catch (E exp)
                
{
                    
if (action != null)
                        action(exp);

                    
return exp as Exception;
                }

            }
;
            
this.list.Add(newFun);
            
return this;
        }


        
/// <summary>
        
/// 添加finally执行区块
        
/// </summary>
        
/// <param name="finallyAction">finally执行区块</param>
        
/// <returns>返回自身,方便快速设置</returns>

        protected TryBlockBase _finally(Action<object> finallyAction)
        
{
            
this.finallyAction = finallyAction;
            
return this;
        }


        
/// <summary>
        
/// 执行实际代码块
        
/// </summary>
        
/// <param name="action">实际代码匿名执行函数</param>
        
/// <returns>有否出现异常</returns>

        protected bool execute(Action<object> action)
        
{
            
if (this.list.Count == 0)
                
this._catch<Exception>(null);

            
this.work = action;

            
this.depth = this.list.Count;
            
this.Exception = this.list.Last()(null);

            
if (this.finallyAction != null)
                
this.finallyAction(null);

            
return this.Faulted;
        }

    }


    
/// <summary>
    
/// 异常捕捉执行块
    
/// </summary>

    public class TryBlock : TryBlockBase
    
{
        
/// <summary>
        
/// 添加异常处理区块
        
/// </summary>
        
/// <typeparam name="E">异常泛型类型</typeparam>
        
/// <param name="action">异常处理匿名执行函数</param>
        
/// <returns>返回自身,方便快速设置</returns>

        public TryBlock Catch<E>(Action<E> action) where E : Exception
        
{
            
return base._catch<E>(action) as TryBlock;
        }


        
/// <summary>
        
/// 添加异常处理区块
        
/// </summary>
        
/// <typeparam name="E">异常泛型类型</typeparam>
        
/// <returns>返回自身,方便快速设置</returns>

        public TryBlock Catch<E>() where E : Exception
        
{
            
return this.Catch<E>(null);
        }


        
/// <summary>
        
/// 添加finally执行区块
        
/// </summary>
        
/// <param name="finallyAction">finally执行区块</param>
        
/// <returns>返回自身,方便快速设置</returns>

        public TryBlock Finally(Action<object> finallyAction)
        
{
            
return base._finally(finallyAction) as TryBlock;
        }


        
/// <summary>
        
/// 执行实际代码块
        
/// </summary>
        
/// <param name="action">实际代码匿名执行函数</param>
        
/// <returns>有否出现异常</returns>

        public bool Execute(Action<object> action)
        
{
            
return base.execute(action);
        }


        
静态属性和静态函数
    }

      使用的效果呢,如下: 

public partial class frmTryBlock : Form
    
{
        
private TryBlock tbCatchSome;

        
public frmTryBlock()
        
{
            InitializeComponent();

            
this.tbCatchSome = TryBlock.Create()
                .Catch
<OverflowException>(f =>
                
{
                    MessageBox.Show(
"出现了溢出异常: " + f.Message);
                }
)
                .Catch
<InvalidCastException>(f => MessageBox.Show("出现了非法转换异常: " + f.Message))                
                .Catch
<InvalidEnumArgumentException>()
                .Finally(f 
=> MessageBox.Show("Finally测试:)"));
        }



private void btnCatchSome_Click(object sender, EventArgs e)
        
{
            
if (this.tbCatchSome.Execute(f =>
                
{
                    
//throw new OverflowException();        //测试1
                    
//throw new InvalidCastException();     //测试2      
                    object obj = null;                      //测试3
                    var a = obj.ToString();                    
                }
))
                
return;

            MessageBox.Show(
"正常工作");
        }


}

    

     代码并未经过严格的测试,这里只是提出这一种思路和做法,我不奢望他能完全替代Try/Cacth,只是希望在很多需要重复使用的场合,可以利用他来简单快捷的撰写安全的代码。
   
    从效率而言,如果这个思路是可行的,我觉得肯定要换成Emit实现的版本。另外,这种方式也应该可以很方便地和EHAB一起使用。

    希望各位朋友不吝赐教:)


 

posted on 2008-04-10 23:15 timiil 阅读(2116) 评论(16)  编辑 收藏 网摘

FeedBack:
2008-04-10 22:32 | deerchao      
效率上我觉得是可行的,毕竟代码中常用的调用栈深度也就几是几层到十几层而已,一处要处理的异常也就一种到七八种,应该对性能的影响不会太大.

而且把异常处理机制放到对像里之后,最大的好处就是可以少写很多代码--把一个异常处理对像扔过去就行了,不必每次都写N个catch.

  回复  引用  查看    
2008-04-10 22:32 | deerchao      
另外,C#到了3.0,终于成了三两天内无法学会所有用法的语言了..
  回复  引用  查看    
2008-04-10 22:43 | asp.net CMS[未注册用户]
还能这么写:)
学习下
嘿嘿

  回复  引用    
2008-04-10 23:24 | BlueMountain      
学习ing
  回复  引用  查看    
2008-04-11 00:23 | Jeffrey Zhao      
不错不错……不过其实这应该算是“匿名函数”,而Lambda表达式只是一种编译器认识的表现形式而已。匿名函数在C# 2.0里就已经有了。
  回复  引用  查看    
2008-04-11 06:53 | Vincent Yang      
@deerchao
其实学会不会那么难,关键是如何应用和让别人也跟上,这个比较难

  回复  引用  查看    
2008-04-11 07:55 | 双鱼座      
哈哈,虽然实用价值并不是很大,仍然是不错的想法。
  回复  引用  查看    
#8楼[楼主]
2008-04-11 08:08 | timiil      
@Jeffrey Zhao
是的,只是借用Lambda这种方式,最简便地写匿名函数而已,并非用了他的其他什么特性。

@双鱼座
感谢老兄关注:) 现在这段代码还是非常简短,若要以严肃的API来考虑,还是需要进一步改进。 如Emit,如Handler的增删复用等等细节:)

  回复  引用  查看    
2008-04-11 08:11 | 无常      

这里也有个新鲜的用法
通常判断一个控件类型时,我们用样:
void Do(Control c)
{
     Label l;
     Button b;
     if ((l as Label) != null)
     {
          // ...
     }
     else if ((c as Button) != null)
     {
          // ...
     }
     else
     {
          // ...
     }
}

使用扩展的Switch可以这样.

void Do(Control c)
{
     new Switch(c)
          .Case<Label>(l =>
          {
               // ...
          })
          .Case<Button>(b =>
          {
               // ...
          })
          .Default(cc =>
          {
               // ...
          });
}

普通的switch用这个替换
void
Do(string name)
{
     new Switch<string>(name)
          .Case(s => s.StartsWith("B"), s =>
          {
               Console.WriteLine(s + " starts with B.");
          }, true)
          .Case(s => s.StartsWith("Ba"), s =>
          {
               Console.WriteLine(s + " starts with Ba.");
          })
          .Default(s =>
          {
               Console.WriteLine(s + " starts with who knows what.");
          });
}
详细看原文。

http://community.bartdesmet.net/blogs/bart/archive/2008/03/30/a-functional-c-type-switch.aspx
 


  回复  引用  查看    
#10楼[楼主]
2008-04-11 08:36 | timiil      
@无常
甚妙。 看来Lambda真是“广阔天地,大有可为”,嘻嘻

  回复  引用  查看    
2008-04-11 09:01 | 装配脑袋      
throw不接参数,重新抛出的异常是带有完整的call stack等信息的。
  回复  引用  查看    
2008-04-11 09:17 | 忍者[未注册用户]
换汤不换药
  回复  引用    
2008-04-11 09:39 | lost[未注册用户]
感觉以这个思路再深入一下应该可以搞出点实用的东西来
  回复  引用    
2008-04-11 09:43 | henry      
妙就妙在MS把匿名函数简化编写的,
其实Func只是为了扩展定义的delegate,其实可以自己定义delegate来代替。

  回复  引用  查看    
#15楼[楼主]
2008-04-11 10:19 | timiil      
@装配脑袋
是的装配兄,是带有;不过在设计器内还不是原地抛出,而是在Catch段:(

  回复  引用  查看    
2008-04-11 11:44 | SZW      
@楼主
@无偿
好就一个字,我只说一次

  回复  引用  查看    



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1147468




相关文章:

相关链接:

<2008年4月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

与我联系

搜索

 

常用链接

留言簿

我参加的小组

我参与的团队

随笔档案

开发团队

猫朋猫友

积分与排名

  • 积分 - 7160
  • 排名 - 5744

最新评论

阅读排行榜

评论排行榜