Lambda来了很久了,他有许多特性。其中之一,可以说是一种快速撰写匿名委托/函数的语法吧。最近偶然拍脑袋,觉得可以用匿名委托来实现一下
ExceptionHandling,希望能得到轻量,可任意扩展,可重用的效果。所以特别撰写本文,望与诸君一起讨论一下,这种方式的实现究竟有无实际意义。
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 { get; set; }
public Action<TException> Handler { get; set; }
public Exception Exception { get; private 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
{ get; protected 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);
}


静态属性和静态函数#region 静态属性和静态函数


/**//// <summary>
/// 创建异常捕捉执行块实例
/// </summary>
/// <returns></returns>
public static TryBlock Create()

{
return new TryBlock();
}


/**//// <summary>
/// 上次执行Quick系列函数获得的异常对象
/// </summary>

public static Exception LastException
{ get; private set; }


/**//// <summary>
/// 快速执行异常捕捉执行块
/// </summary>
/// <param name="action">实际代码匿名执行函数</param>
/// <returns></returns>
public static bool Quick(Action<object> action)

{
var tb = new TryBlock();
var ret = tb.Execute(action);
TryBlock.LastException = tb.Exception;
return ret;
}

#endregion
}
使用的效果呢,如下:
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一起使用。
希望各位朋友不吝赐教:)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix