C # series of articles,

Source: Internet
Author: User

C # series of articles,

Content of the file:

  • Design public event types
  • How the compiler implements events
  • Design the types of listening events
  • Explicit event implementation

Event: defines the type of event members that can be used to notify other objects of specific events.

The CLR event model is based on delegation. Delegation is a type of secure method for calling callback methods. Objects receive notifications subscribed to by them by calling methods.

Defining the type requirements of event members provides the following functions:

This article uses an email application as an example. When the email arrives, the user wishes to forward the email to the fax machine or pager for processing. First, design the MainlManager type to receive incoming emails. It discloses the NewMain event. Other types of (Fax or Pager) object registration are concerned about this event. When MailManager receives a new email, this event is triggered. As a result, the email is sent to each registered object, which can process the email in its own way.

1.1 design the types of events to be published

Step 1: Define a type to accommodate all additional information that needs to be sent to the Event Notification recipient

This type usually contains a set of private fields and read-only public attributes used to publish these fields.

 1 class NewMailEventArgs:EventArgs 2     { 3         private readonly string m_from, m_to, m_subject; 4         public NewMailEventArgs(string from,string to,string subject) 5         { 6             m_from = from; 7             m_to = to; 8             m_subject = subject; 9         }10         public string From { get { return m_from; } }11         public string To { get{ return m_to; } }12         public string Subject { get { return m_subject; } }13     }

Step 2: Define event members

class MailManager    {        public event EventHandler<NewMailEventArgs> NewMail;    }

NewMail is the event name. If the event member type is EventHandler <NewMailEventArgs>, all the recipients of the Event Notification must provide a callback method that matches the prototype with the delegate type. The generic System. EventHandler delegate type is defined as follows:

Public delegate void EventHandler <TEventArgs> (Object sender, TEventArgs e );

Therefore, the method prototype must take the following form: void MethodName (Object sender, NewMailEventArgs e); the event mode requires that the return type of all event handlers be void, it is because several callback methods may be called after an event is triggered, but the return values of all methods cannot be obtained. The returned void does not allow the callback method to return values.

Step 3: Define a method to notify the event registration object

1 // <summary> 2 // define the method to notify the event registration object, which is defined in MailManager 3 // if the class is sealed, this method should be declared as private and non-virtual 4 // </summary> 5 // <param name = "e"> </param> 6 protected virtual void OnNewMail (NewMailEventArgs e) 7 {8 // for thread security considerations, copy the reference of the delegate field to 9 EventHandler <NewMailEventArgs> temp = Volatile in a temporary variable. read (ref NewMail); 10 if (temp! = Null) 11 {12 temp (this, e); 13} 14}

The above method uses the Volatile. Read () method to ensure thread security, mainly considering the following two situations:

1. Determine NewMail directly! = Null, but before NewMail is called, another thread may remove a delegate from the delegate chain to make it null, resulting in an (NullReferenceException) exception.

2. some may also save it in a temporary variable, but do not use Volatile. Theoretically, but if the compiler optimization Code removes the temporary variable, it will be the same as the first case.

Using Volatile. Read will force NewMail to Read this call, and the reference must be copied to the temp variable. This is a perfect solution. But this does not happen in a single thread.

Step 4 define a method to convert input into expected events

1 public void SimulateNewMail (string from, string to, string subject) 2 {3 // construct an object to accommodate the information to be sent to the notification recipient. 4 NewMailEventArgs e = new NewMailEventArgs (from, to, subject ); 5 // call the virtual method to notify the object that the event has been reversed 6 // if there is no type to rewrite this method 7 // Our object will notify all the registered objects of the event 8 OnNewMail (e ); 9}

This method indicates that a new email has arrived at MailManager.

1.2 How the compiler implements events

In the MailManager class, we define the event member itself in one sentence: public event EventHandler <NewMailEventArgs> NewMail;

C # The Compiler converts the code to the following:

// Private EventHandler <NewMailEventArgs> NewMail = null; public void add_NewMail (EventHandler <NewMailEventArgs> value) {// calls CompareExchange through loops and, to add a delegate to an event in a thread-safe way // CompareExchange is to compare the destination operand (the number of memories that the 1st parameter points to) // with a value (the 3rd parameter, if they are equal, // The EventHandler <NewMailEventArgs> prevHandler is exchanged with the target operand (the number in memory pointed to by the 2nd parameter; eventHandler <NewMailEventArgs> newMail = this. newMail; do {prevHandler = n EwMail; EventHandler <NewMailEventArgs> newHandler = (EventHandler <NewMailEventArgs>) Delegate. combine (prevHandler, value); newMail = Interlocked. compareExchange <EventHandler <NewMailEventArgs> (ref this. newMail, newHandler, prevHandler);} while (newMail! = PrevHandler);} public void remove_NewMail (EventHandler <NewMailEventArgs> value) {EventHandler <strong> prevHandler; EventHandler <NewMailEventArgs> newMail = this. newMail; do {prevHandler = newMail; EventHandler <NewMailEventArgs> newHandler = (EventHandler <NewMailEventArgs>) Delegate. remove (prevHandler, value); newMail = Interlocked. compareExchange <EventHandler <NewMailEventArgs> (ref th Is. NewMail, newHandler, prevHandler);} while (newMail! = PrevHandler );}

In this example, the accessibility of the add and remove methods is public because the NewMail event is declared as public. the accessibility of the event determines what code can be used to register and deregister the attention to the event. However, in any case, only the type itself can access the aforementioned delegated field NewMail. In addition to the code generated above, the compiler also generates event definition record items in the metadata of the hosted assembly. It contains some flags and basic Delegate types. The CLR itself does not use the metadata information to run only the accessors method.

1.3 design the types of listening events

How to define one type to use the events provided by another type. Take the Fax type as an example:

Internal class Fax {public Fax (MailManager mm) {// register our callback method mm with the NewMail event of MailManager. newMail + = FaxMsg;} // when a new email arrives, MailManager will call this method // sender to indicate the MailManager object, it is easy to return information to it. // e indicates the additional event information that the MailManager object wants to send to us. private void FaxMsg (object sender, NewMailEventArgs e) {Console. writeLine ("Fax message from: {0} to: {1} subject: {2}", e. from, e. to, e. subject );} /// <summary> /// logout /// </summary> /// <param name = "mm"> </param> public void Unregister (MailManager mm) {mm. newMail-= FaxMsg ;}}

When the email application initializes, it first constructs the MailManager object and saves the reference to this object to the variable. Then construct the Fax object and pass the MailManager object reference as a real parameter. In the Fax constructor, use + = to register the attention to the NewMail event.

1.4 explicit event implementation

About 70 events are defined for the System. Windows. Forms. Control type. Every object created from the Control-derived type wastes a lot of memory, and most of us only care about a few events. The idea of how to efficiently implement a large number of events through explicit implementation of events is as follows:

When defining an event: each object of a public event must maintain a set (such as a dictionary ). The Set uses an event identifier as the key and the delegate list as the value. When the new object is constructed, the set is also blank. When you register an event, you will find the event identifier in the collection. If the event identifier exists, the new delegate is merged with the delegate list of the event. Otherwise, the event identifier and delegate are added.

When an event is triggered: the object-triggered event searches for the identifier of the event in the collection. If it does not indicate that the event is not followed by the object registration, callback is not required. Otherwise, the list of associated delegates will be called.

1 public sealed class EventKey {} 2 public sealed class EventSet 3 {4 // define private Dictionary 5 private readonly Dictionary <EventKey, Delegate> m_events = 6 new Dictionary <EventKey, delegate> (); 7 /// <summary> 8 // Add if no one exists, if yes, merge it with the existing EventKey 9 // </summary> 10 public void Add (EventKey eventKey, Delegate handler) 11 {12 // ensure that the operation is unique 13 Monitor. enter (m_events); 14 Delegate d; 15 // obtain the value 16 m_events.TryGetValue (eventKey, ou T d); 17 // Add or merge 18 m_events [eventKey] = Delegate. combine (d, handler); 19 Monitor. exit (m_events); 20} 21 // <summary> 22 // Delete the delegate, when deleting the last Delegate, you also need to delete the dictionary's EventKey-> Delegate23 // </summary> 24 public void Remove (EventKey eventKey, Delegate handler) 25 {26 Monitor. enter (m_events); 27 Delegate d; 28 // TryGetValue ensure that the exception 29 if (m_events.TryGetValue (EventKey, out d) is not thrown when you try to delete the nonexistent eventKey from the set )) 30 {31 d = Delegate. remove (d, Handler); 32 if (d! = Null) 33 {34 // if there is a delegate, set the new header 35 m_events [eventKey] = d; 36} 37 else38 {39 m_events.Remove (eventKey ); 40} 41} 42 Monitor. exit (m_events); 43} 44 // <summary> 45 // triggers event 46 for the specified EventKey /// </summary> 47 public void Raise (EventKey eventKey, object sender, EventArgs e) 48 {49 Delegate d; 50 Monitor. enter (m_events); 51 m_events.TryGetValue (eventKey, out d); 52 Monitor. exit (m_events); 53 if (d! = Null) 54 {55 // using DynamicInvoke, the type of the parameter is verified to the called callback method. 56 // call the method. If the type does not match, an exception is thrown. 57 d. dynamicInvoke (new Object [] {sender, e}); 58} 59} 60}

Next, define the class to use EventSet.

1 public class FooEventArgs: EventArgs {} 2 public class TypeWithLotsOfEvents 3 {4 // used to manage a group of "events/delegates" 5 private readonly EventSet m_eventSet = new EventSet (); 6 // protected attributes enable the derived type to access the set 7 protected EventSet {get {return m_eventSet ;}} 8 // construct a static read-only object to identify this event 9 // each object has its own hash code, in order to find the delegate linked list of this event in the collection of objects 10 protected static readonly EventKey s_fooEventKey = new EventKey (); 11 // define the event accesser method, used to add or delete the delegate 12 public event EventHandler <FooEventArgs> Foo13 {14 add {m_eventSet.Add (s_fooEventKey, value);} 15 remove {m_eventSet.Remove (s_fooEventKey, value );} 16} 17 // define the protected virtual Method 18 protected virtual void OnFoo (FooEventArgs e) 19 {20 m_eventSet.Raise (s_fooEventKey, this, e) for this event ); 21} 22 // define the method for converting input to this event 23 public void SimulateFoo () {OnFoo (new FooEventArgs ();} 24}

To use TypeWithLotsOfEvent, you only need to register with the event according to the standard syntax.

1 static void Main (string [] args) 2 {3 TypeWithLotsOfEvents twle = new TypeWithLotsOfEvents (); 4 twle. foo + = HandleFooEvent; 5 twle. simulateFoo (); 6 Console. read (); 7} 8 9 private static void HandleFooEvent (object sender, FooEventArgs e) 10 {11 Console. writeLine ("succeeded"); 12}

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.