Original article: http://www.atmarkit.co.jp/fdotnet/introrx/introrx_01/introrx_02_02.html
Author: Hehe Yiwen
What is an event? Advantages of using RX to handle events
Let's take a look at how to use the RX-specific event obserable to convert the event for processing. Before that, let's consider the application scenarios of events in. net.
The most representative is the GUI event. For example, Button clicking and mouse moving are all handled by events. In windowsphone (simplified as WP7), gesture input, such as touch, are all event processing. Like tabletpc and WP7, it is also equipped with sensors, as well as the recently popular Kinect device, the sensor is also configured.
In addition, there is a purpose for notification (push). For example, the inotifypropertychanged interface uses events to notify attributes of changes. In addition, the filesystemwatcher class monitors file or folder changes and sends notifications through events. Other timer events, WebClient asynchronous methods, and so on are also handled by events.
Gui events: merging
So what is better to use RX to operate events? If you want to talk about it, you can first "synthesize" it ". For example, in a GUI event, when you press, move, or open the mouse to combine these operations, a flag is used for external management, this may make the code more complex and less readable, and in addition to this combination, it is very easy to destroy the original code structure if you want to process something else in the press/move/release event.
However, if you use Rx for operations, you do not need to use external flags. You can directly combine the press, move, and release into a new event. In this way, you can handle events by pressing, moving, or releasing the event without being controlled by the flag. The code will not be mixed together, which is more concise. In addition, the readability of merged events is greatly improved.
The following code example shows how to merge and process a new event by pressing, moving, or releasing it:
// Drag event of Windows form: Press/move with the left mouse button until the mouse is opened. // obtain the coordinates of the mouse. var drag = from down in this. mousedownasobservable () from move in this. mousemoveasobservable (). takeuntil (this. mouseupasobservable () Select move. location;
* The mousedownasobservable, mousemoveasobservable, and mouseupasobservable methods are expansion methods packaged using the fromevent static method of the observable class, which will be described later.
In addition, for simple event processing, you can only use the onnext method of the iobservable <t> object. Rx is used to process events, the oncompleted method, and the onerror method are complete, which is better than general events.
Timer/notification event
Consider timer. For example, polling is used to monitor a value at a certain interval. RX converts timer into a sequence, and flexibly transforms the output using the select method. When these are combined, the value of the Monitored object is automatically pushed over a certain period of time, which is very easy to operate. The processing of the filter value is also very simple. It is easier to receive the filter value only when the value changes.
The following is the sample code for RX to generate a value and filter timer in a certain period of time.
// Monitor watchtarget every 1 second. value var polling = observable. timer (timespan. zero, timespan. fromseconds (1 )). select (_ => watchtarget. value ). distinctuntilchanged (); // The event is triggered only when the value changes (polling)
Because it is easy to use Rx for filtering, you can easily choose and adjust the handling of a large number of notification events in gestures and sensors. In addition, RX not only removes the IF... else statement, but also filters by time. The following is the sample code filtered by Time:
// The changed event of filesystemwatcher // a change will trigger multiple events var watcher = new filesystemwatcher ("C: \", "test.txt") {enableraisingevents = true }; // "filter events that occur consecutively within one second and only process the last one" // convert them into a relatively easier object var changed = observable. fromeventpattern <filesystemeventargs> (watcher, "changed "). throttle (timespan. fromseconds (1); // The throttle method allows only the content of a specified time and value (as the name suggests: valve)
Unittest
The feature of Rx is that it can handle events freely. Using RX can solve the unittest of originally difficult events. For example, a value "10" is generated at 3 minutes 30 seconds, and a value "20" is generated at 4 minutes 0 seconds. Rx can simulate such a combination of time and value events. The methods used for these tests are all in the Microsoft. Reactive. Testing assembly (using nuget "RX-testing "). The unittest of RX will be detailed later.
Fromevent method and fromeventpattern Method
To process events with RX, you must use the fromevent method or the fromeventpattern method to change the events to an iobservable <t> object. The fromevent method can convert the action <t> proxy, and the sequence element is T. The fromeventpattern method can be used to convert the eventhandler proxy, while the sequence element is eventpattern <teventargs>. It encapsulates the sender and teventargs e of the object type.
Event RX Transformation: Specify the event name
Let's take a look at the fromeventpattern method.
// WPF/Silverlight/WP7 button control ("button1") The Click Event RX observable. fromeventpattern <routedeventargs> (button1, "click ");
This is the simplest method for event RX transformation. The type parameter of the fromeventpattern method is of the eventargs type. (Routedeventargs in this example). The first parameter is used to pass in the control instance, and the second parameter is used to specify the event name (click in this example ). As you can see, because the event name is a string, the processing in it should be done through reflection. The non-Reflection Method (which will be introduced later) is simple and easy to remember. In addition, this transformation is not frequent (once only), and the effect on the performance is negligible. So there is no problem with this.
Event RX Transformation: Specifies the event processing method (non-Reflection Method)
If you do not use reflection for RX transformation, follow these steps:
// The first parameter is the Event Response Processing proxy (H => H. invoke is fixed) // The second parameter binding event, and the third parameter unbinds the event observable. fromeventpattern <routedeventhandler, routedeventargs> (H => H. invoke, H => button1.click + = H, H => button1.click-= H );
The amount of code is slightly increased, but this is the best practice for performance considerations. In addition, the fromeventpattern method has several reloads.
How to clear the RX transformation of an event
The processing of the specified event, as well as the specified string, will be more encoded. In actual use, this will affect the maintainability of the Code. Therefore, we recommend that you separate the Code as an extension method.
Public static class buttonbaseextensions {// separated Extension Method public static iobservable <routedeventargs> clickasobservable (this buttonbase button) {return observable. fromeventpattern <routedeventhandler, routedeventargs> (H => H. invoke, H => button. click + = H, H => button. click-= H ). select (x => X. eventargs) ;}}// call button1.clickasobservable () in actual use (). subscribe (_ => MessageBox. show ("clicked! "));
In the preceding example, select the eventargs object from the eventpattern <teventargs> object. In the processing method of event registration in the example, the object sender is the extension method object itself (button1) and does not need to be passed, only the eventargs type E is passed, it is simpler.
(Data Developer Center) The fromevent method is similar to the fromeventpattern method. The object event type is action <t>. Although. in the. NET Framework, although action <t> is not the proxy type corresponding to the standard event, there are not many direct use cases, however, it can be used for developer-defined action events.
In addition, the sender parameter can be omitted by changing the proxy, just like in the preceding example.
Public static class buttonbaseextensions {// use fromevent to generate the eventpattern object // use select to omit sender public static iobservable <routedeventargs> clickasobservable (this buttonbase button) {return observable. fromevent <routedeventhandler, routedeventargs> (H => (sender, e) => H (E), H => button. click + = H, H => button. click-= h );}}
If the sender parameter is necessary, you can repackage it using select. For example:
Button1.clickasobservable (). Select (EV => New {sender = button1, eventargs = EV })
Next, we will further introduce how to synthesize Rx.