随笔 - 46  文章 - 0  评论 - 136  阅读 - 11万

委托异步调用时BeginInvoke的陷阱处理

这个陷阱来自于一个需求:需要异步在后台处理数据,处理完后触发处理完成的事件,大概是这么写的:

EmployeeCollection data = new EmployeeCollection();
data.Loaded += data_Loaded;
Action<EmployeeCollection> action = (d) => {
    DalHelper.Fill(data);
    data.RaiseEventLoaded();
};
action.BeginInvoke(data, null, null);

挺简单的代码,陷阱也在其中。假如DalHelper.Fill(data)抛出了一个异常,那么对data.RaiseEventLoaded()就不会执行,依赖于data.Loaded事件的代码也不会执行,这是一个bug,应该在委托执行中加入一个try...catch语句,或者在某个地方调用委托的EndInvoke方法,来处理执行中可能的异常。

 

为了这么一个简单的需求,加入try...catch或者调用委托的EndInvoke都太复杂了,仅仅只想满足假如执行失败,就把异常抛出来,即使将当前进程结束也没事。本着一次编写,多次使用的原则,专门设计了一个帮助类来专职这类委托的异步调用。帮助类的代码如下:

复制代码
public class EventHelper {
   public static void UnsafeBeginInvoke(Delegate del,params object[] args){
      AsyncFire asyncFire = InvokeDelegate;
      asyncFire.BeginInvoke(del, args, ThrowCallback, asyncFire);
   }     

    delegate void AsyncFire(Delegate del,object[] args);

    static void InvokeDelegate(Delegate del,object[] args){
        del.DynamicInvoke(args);
    }

   static void ThrowCallback(IAsyncResult ar) { 
       AsyncFire asyncFire = ar.AsyncState as AsyncFire;
       asyncFire.EndInvoke(ar);
   }
}
复制代码

核心实现是将委托的调用封装起来,在另外一个委托中去调用,然后对另外的那个委托用EndInvoke来释放可能的异常,这样就能够发现单纯的调用BeginInvoke后委托执行时引发的异常。这样修改后,刚才的代码就可以这样来调用:

EmployeeCollection data = new EmployeeCollection();
data.Loaded += data_Loaded;
Action<EmployeeCollection> action = (d) => {
    DalHelper.Fill(data);
    data.RaiseEventLoaded();
};
EventHelper.UnsafeBeginInvoke(action, data);

代码还如最初的设计那么简单,而且真要是委托中发生了异常,也能够发现这个错误,而不是让这个错误被掩盖。

 

另外,刚才的实现不是类型安全的,类型安全可以通过重载来解决,例子如下:

复制代码
public class EventHelper {
   public static void UnsafeBeginInvoke(Delegate del,params object[] args){
      AsyncFire asyncFire = InvokeDelegate;
      asyncFire.BeginInvoke(del, args, ThrowCallback, asyncFire);
   }     

    delegate void AsyncFire(Delegate del,object[] args);

    static void InvokeDelegate(Delegate del,object[] args){
        del.DynamicInvoke(args);
    }

   static void ThrowCallback(IAsyncResult ar) { 
       AsyncFire asyncFire = ar.AsyncState as AsyncFire;
       asyncFire.EndInvoke(ar);
   }

   #region 添加类型安全的委托

   public static void BeginInvoke(Action del){
      UnsafeBeginInvoke(del);
   }

   public static void BeginInvoke<T,U>(Action<T,U> del,T t, U u){
      UnsafeBeginInvoke(del,t,u);
   }

   public static void BeginInvoke<T,U,V>(Action<T,U> del,T t, U u, V v){
      UnsafeBeginInvoke(del,t,u,v);
   }

   #endregion 添加类型安全的委托
}
View Code
复制代码

各位同学可以根据自己的需要添加类型安全的实现。

posted on   ProJKY  阅读(4933)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示