zoukankan      html  css  js  c++  java
  • 委托异步调用时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

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

  • 相关阅读:
    SAP MM 采购发票上的金额小差异
    SAP MM 物料号到物料的库存转移过账里的差异
    SAP MM 采购附加费在收货以及发票过账时候的会计分录
    SAP MM 移动平均价的商品发票价格和采购订单价格差异的处理
    WPF 使用 VisualBrush 在 4k 加 200 DPI 设备上某些文本不渲染看不见问题
    dotnet 写一个支持层层继承属性的对象
    dotnet OpenXML 读取 PPT 内嵌 xlsx 格式 Excel 表格的信息
    WPF 在 .NET Core 3.1.19 版本 触摸笔迹偏移问题
    linux下将编译错误输出到一个文本文件
    浮点型(FLOAT)与CHAR型转换
  • 原文地址:https://www.cnblogs.com/ProJKY/p/begininvokethrowhelper.html
Copyright © 2011-2022 走看看