Limitations and extensions of the. NET event listening Mechanism
. NET regards "events" as a basic programming concept and provides very elegant syntax support. The differences between the two language design ideas can be seen by comparing the following C # and Java code. // C # someButton. click + = OnSomeButtonClick; copy the code // export omebutton. addActionListener (new ActionListener () {public void actionreceivmed (){...}}); in our software, we use a large number of events to decouple the listener and publisher. However, we have encountered some limitations. Here we will share with you one or two. One is that the call sequence of listeners cannot be guaranteed; the other is the efficiency of listening and listening cancellation when many listeners exist. The event listener's call sequence. NET's event listening mechanism does not clearly guarantee the listener's call sequence, but sometimes we need to ensure the processing sequence between different components. For example, we use the interpreter mode in our software to implement user interaction, A component called an interactive source is responsible for assigning events on the UI control to a group of components called an interactive device. These components are given the opportunity to process events in sequence according to their predefined priorities, the lower-priority components can perform further processing only when the high-priority interactive server does not process events. In this way, we can organize the interaction devices in different order to reuse them in the implementation of different business functions. For example, you can reuse some basic view scaling, translation, menu processing, and other functions. In the above scenarios, it is very important to ensure the order of event processing between the interactive devices. Of course, if you take a look at the MulticastDelegate source code, you can know that in the current implementation, each listener still has a certain call order. However, this is the implementation details, which may be changed in the future. Second, if different listeners are located in different modules, it is also very difficult to ensure the call sequence between them by relying on this implementation. Here we refer to the Java method for event processing using interfaces, and receive a parameter indicating priority when adding a listener, so that we can clearly maintain the order of each listener, the following code is used. We define corresponding methods for each UI event in the IInteractor interface, and let InteractSource be responsible for converting the events on the control into calling corresponding methods in the interface. Copy the public class InteractSource {public void AddInteractor (int priority, IInteractor interactor) {}} public interface IInteractor {public void OnMouseDown (MouseEventArgs e ){}......} the efficiency of adding and removing code listeners MulticastDelegate is the implementation behind the event mechanism we usually use. The source code shows that, it uses arrays internally to save references to various listeners. This causes a problem-when the number of listeners for an event is large, the efficiency of adding and removing listeners will be very low. Taking removal as an example, for events with N listeners, an average of N/2 comparisons are required to determine the location of the listener, and additional array sorting operations are required. To solve this problem, we first try to define the logic for adding and removing events, and internally try to store events using dictionaries, hash tables, and other methods. However, it turns out that, although the two have advantages in time complexity, their actual efficiency still does not meet the requirements. It is best to have a data structure that can add and remove listeners within a constant time. You may also think of a two-way linked list. Maybe you think again -- adding and deleting in a two-way linked list is a constant time, but the search is still the complexity of O (n. The interface design method shows its flexibility again. We can design the event publisher as follows (indicating the code ): copy the public class EventSource {private listener list = new listener list (); public Tocken AddListener (IEventListener listener) {listener listnode n = new listener listnode (listener); list. addLast (n); return new Tocken (node);} public void RemoveListener (Tocken tocken) {list. remoe (tocken. node);} public class Tocken {internal initialize listnode node;} copy the code in this class You can use a two-way linked list to store added listeners. When the AddListener method is called each time, the added linked list node is saved to a Token and returned. The listener needs to save the token and use it to cancel the listener. Of course, listeners can ignore what a token is, just like a subway ticket is always just a ticket, and we never care about what information it contains. However, the publisher can save some positioning information in it to make full use of the information when the listener is removed. In the code above, I saved the reference of the linked list node, so that the addition, positioning, and removal of listeners are completed within a constant time. Of course, you can also save the publisher's reference in Tocken, so that you can find a BUG like "canceling listening to an object that has never been listened. Or, there is other information.