zoukankan      html  css  js  c++  java
  • c# Reactive Extension中的FromEventPattern和FromEvent

    一些废话

    在做一个设备的报告同步功能,通过FileSystemWatcher监控设备在指定位置生成的报告,解析并上传。
    之所以监控文件系统,而不是跟设备软件直接对接,又会有一大段牢骚,暂且不提。

    而监控文件时,文件的修改创建等事件,有时会被多次上报,这可能是文件系统或者代码的实现相关,所以要做防抖动。

    之前通过自定义的私有字段/定时器做过防抖动,有些麻烦,而且不是很容易维护,在网上了解到Reactive Extension(对应.net core中的System.Reactive包),可以做防抖,于是找相关文档读了一下。

    一开始看的一头雾水,主要是功能太多了,主要目的也不是防抖的,所以很不容易找到想要的内容(简单直接的例子)

    正文

    Observable从事件中创建,这里有两个创建方式FromEventPatternFromEvent(其实还有很多,但是我涉及的是这两个),找了半天相关资料(SO上的这个回答帮助很大),并且写了一些测试代码,总算是明白大概咋回事了。

    最终代码

    先贴上最终可以使用的代码方法(其实正确方式不止一种)

    var watcher = new FileSystemWatcher();
    watcher.Path = "d:\test";
    
    var observable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
                ev => watcher.Changed += ev,
                ev => watcher.Changed -= ev);
    
    var d = observable.Throttle(TimeSpan.FromSeconds(5))
        .Subscribe(
            a => Console.WriteLine($"Watcher type:{a.EventArgs.ChangeType }, file:{a.EventArgs.Name }, sender: {a.Sender}"),
            ex => Console.WriteLine("observer error:" + ex.ToString()),
            () => Console.WriteLine("observer complete.")
    

    Observable的实现理解

    Observable用于绑定Event源和Observer中的delegate,他的作用在于可以进行一些过滤处理(个人理解)。

    其中Observer主要通过实现OnNext这一个delegate,当Observable有事件触发时,就会调用Observer注册的OnNext方法,OnNext方法是Action<T>类型的,而模板参数T来自Observable创建时指定。

    .net 的事件

    .net中的事件处理函数符合EventHandler这个delegate格式

    public delegate void EventHandler(object sender, EventArgs e);
    

    传入两个参数,返回为空。

    Observable用于将事件处理分发给observer,虽然事件可以是任何自定义事件,但是对于符合.net标准的EventHandler模式事件,提供了FromEventPattern方式创建Observable,而其他自定义模式的事件,需要使用FromEvent进行创建。

    两者最终功能是等同的,用FromEvent也可以做到FromEventPattern的功能(需要更多的代码),反之亦然(更麻烦),我写了几个例子来测试这一点。

    FromEvent

    两者的签名(选取了通用的一种)

    //FromEvent
    public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>
                  (Func<Action<TEventArgs>, TDelegate> conversion, 
                  Action<TDelegate> addHandler,
                  Action<TDelegate> removeHandler);
    

    这是通用的Observable创建方法。

    • TDelegate: 这是事件源的类型

    • TEventArgs: 这实际上是OnNext中的T参数,而不是TDelegate的参数OnNextAction<T>,只能接收一个参数,这个泛类型就指明了Observer创建的OnNext处理函数中需要处理的参数

    • conversion: 通常来说TDelegateOnNext签名是不同的,此时需要一个转换函数,将Tdelegate转化为对OnNext的调用

    • addHandler/removeHandler: 注册和取消注册事件,在Observer调用Subscribe订阅时注册。

      实际上可以看出来,Observable将源事件(符合类型TDelegate)类型,转化为对OnNext的调用。
      某种程度上来说,可以看做event+操作(可能会有疑问那为何要rx这一层,observer实际能力是在对事件的过滤处理上,即在事件->事件处理函数的路径上,增加了各种机能)。

    FromEventPattern

    //FromEventPattern
    public static IObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler);
    

    该签名和FromEvent基本相同,区别在于少了一个convension转换参数。

    这是因为该函数设计即是用于对接.net 标准的EventHandler类型的事件。EventHandler中包含两个参数senderEventArgs,而这个函数即会将sender和EventArgs两个参数构造为一个EventPattern<EventArgs>类型的参数,传递给OnNext
    也就是说,RX内部帮助我们实现了对于.net 标准的EventHandler类型事件的conversion转换函数。

    注意到这里也有TDelegateTEventArgs两个参数,这帮助我们扩展符合EventHandler类型事件的扩展格式。EventHandler中的EventArgs可以被指定为TEventArgs代表的类型,EventHandler也可以被相应扩展为EventHandler<TEventArgs>类型的事件格式。

    示例代码

    我基于我的原本需求FileSystemWatcher监控,写了一些示例代码来验证使用方式和差别。

    Observable基于FileSystemWatcher的Changed事件,Observer的OnNext实现全部使用相同的打印函数,而通过不同的Observerbal创建方法,达成同样的目的。

    最后还增加了一个同时监控ChangedRenamed事件,并且增加我最初防抖动目的的代码。

    框架代码如下:

    //Create watcher
    var watcher = new FileSystemWatcher();
    watcher.Path = "d:\test";
    
    //creation of observable goes here
    var observable = .....
    
    //subscribe to observable
    var d = observable
    	.Subscribe(
    		a => Console.WriteLine($"Watcher type:{a.ChangeType}, file:{a.Name}"),
    		ex => Console.WriteLine("observer error:" + ex.ToString()),
    		() => Console.WriteLine("observer complete.")
    );			
    //Enable watcher
    watcher.EnableRaisingEvents = true;
    

    FromEventPattern

    由于Changed事件签名符合EventHandler<FileSystemEventArgs>类型,因此通过这种方式可以如下定义observable:

    var observable = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
                                ev => watcher.Changed += ev,
                                ev => watcher.Changed -= ev).Select(x => x.EventArgs);
    

    注意这里最后的Select,它将EventPattern<FileSystemEventArgs>转化为FileSystemEventArgs

    FromEvent

    如果通过FromEvent,则需要将EventHandler<FileSystemEventArgs>转化为对OnNext的调用。
    如果不通过conversion函数使用FromEvent,则在代码运行时创建代理会失败:

    //THIS IS WRONG!
    var observable = Observable.FromEvent<FileSystemEventHandler, FileSystemEventArgs>(
                                ev => watcher.Changed += ev,
                                ev => watcher.Changed -= ev);
    

    通过conversion

    需要增加一个conversion函数转化

    var observable = Observable.FromEvent<FileSystemEventHandler, FileSystemEventArgs>(
                        handler =>
                        {
                            return (object sender, FileSystemEventArgs fa) => handler(fa);
                        }
                        , ev => watcher.Changed += ev,
                                ev => watcher.Changed -= ev);
    

    通过修改源代理类型

    稍微麻烦,只是作为展示FromEvent的使用,当签名相同时,可不用conversion重载。

    首先定义observable使用的代理类

    private delegate void MyWatcherDelegate(FileSystemEventArgs args);
    
    private static event MyWatcherDelegate MyWatcherEvent;
    

    然后将watcher的事件订阅到自定义的MyWatcherEvent事件中.(注意,由于这里使用的是匿名函数,所以无法从Watcher的事件中删除)

    watcher.Changed += (sender, args) =>
                    {
                        Console.WriteLine("Intermediate watcher handler delegate");
                        if (null != MyWatcherEvent)
                        {
                            Console.WriteLine("Call through custom event");
                            MyWatcherEvent(args);
                        }
                    };
    

    最后通过FromEvent创建Observable,这时用于MyWatcherDelegate符合OnNext的签名,因此不需要conversion方法,也可以成功创建。

    var observable = Observable.FromEvent<MyWatcherDelegate, FileSystemEventArgs>(
                       ev => MyWatcherEvent += ev,
                       ev => MyWatcherEvent -= ev);
    

    合并Renamed和Changed事件

    这里其实和标题关系不大,而是扩展Observable的使用。
    通过Merge能使observable同时监听两个签名不同的事件:

    var o1 = Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
                                ev => watcher.Changed += ev,
                                ev => watcher.Changed -= ev);
    var o2 = Observable.FromEventPattern<RenamedEventHandler, RenamedEventArgs>(
                ev => watcher.Renamed += ev,
                ev => watcher.Renamed -= ev)
        .Select(x => new System.Reactive.EventPattern<FileSystemEventArgs>(x.Sender, x.EventArgs));
    
    var observable = Observable.Merge(o1, o2);
    
  • 相关阅读:
    移动端1px问题
    js几种数组排序及sort的实现
    从零开始搭建vue移动端项目到上线
    Maven项目常见错误解决方法汇总
    重读《Java编程思想》
    ubuntu开发环境下eclipse的alt+/自动补全功能不能用
    Linux环境下解压rar文件
    Ubuntu 16.04下deb文件的安装
    优化Ubuntu 16.04系统的几件事
    Ubuntu16.04 安装 “宋体,微软雅黑,Consolas雅黑混合版编程字体” 等 Windows 7 下的字体
  • 原文地址:https://www.cnblogs.com/mosakashaka/p/12608853.html
Copyright © 2011-2022 走看看