C # Series article Events

Source: Internet
Author: User
Tags throw exception
What the file covers:

    • Design public event Types

    • How the compiler implements events

    • Types of Design Listening events

    • Implementing an event explicitly

Event: The type that defines the event member allows the type to notify other objects that a particular thing has happened.

The CLR event model is based on a delegate, which is a type-safe way to invoke a callback method in which the object receives notification of their subscription by invoking a method.

The type requirements that define the event members provide the following capabilities:

    1. Method can register its attention to the event

    2. Method can unregister its attention to the event

    3. When an event occurs, the registered method receives a notification

This article takes an e-mail application as an example. When an email arrives, the user wants to forward the message to a fax machine or pager for processing. The Mainlmanager type is designed to receive incoming e-mail messages, and it exposes Newmain events. Other types (fax or pager) Objects register concern for the event. Mailmanager receives a new e-mail message that raises the event, causing the message to be distributed to each registered object, all of which have their own way of handling the message.

1.1 Design the type of event to expose

First step: Define the type to accommodate all additional information that needs to be sent to the event notification recipient

This type typically contains a set of private fields and some read-only public properties that are used to expose those fields.


1 class Newmaileventargs:eventargs 2     {3         private readonly string m_from, M_to, m_subject; 4 public         Newmaileve Ntargs (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;}} One public         string to {get{return m_to;}} Public         string Subject {get {return m_subject;}} -     }

Step Two: Define event members


Class Mailmanager    {public        event eventhandler<newmaileventargs> NewMail;    }

Where NewMail is the event name. The event member type is eventhandler<newmaileventargs> all recipients that describe the event notification must provide a prototype and a callback method that matches its delegate type. Because the generic System.EventHandler delegate type is defined as follows:

public delegate void Eventhandler<teventargs> (Object sender,teventargs e);

Therefore, the method prototype must have the following form: void MethodName (Object Sender,newmaileventargs e); The event pattern requires that all event handlers return types are void, Because it is possible to invoke several callback methods after an event is raised, there is no way to get the return value of all methods, and returning void does not allow the callback method to have a return value.

Step three: Define the registration object that is responsible for raising the event's method to notify the event


1         //<summary> 2 Define the method that is         responsible for raising the event to notify the event's enlistment object, which is defined in Mailmanager 3         ///If the class is sealed, the method is declared private and non-virtual 4         // /</summary> 5//         <param name= "E" ></param> 6         protected virtual void Onnewmail ( Newmaileventargs e) 7         {8             //For thread-safety reasons, the reference to the delegate field is now copied to a temporary variable 9             eventhandler<newmaileventargs> temp = Volatile.read (ref NewMail),             if (temp!=null) one             {each                 temp (this, e);             }14         }

The above method uses the Volatile.read () method to ensure thread safety, mainly considering the following two scenarios:

1. Directly judge Newmail!=null, but before calling NewMail, another thread may remove a delegate from the delegate chain, leaving it empty, resulting in an (NullReferenceException) exception.

2. Some people may also save it in a temporary variable, but not using volatile, theoretically, but if the compiler has optimized code to remove the temporary variable, it will be the same as in the first case.

Using Volatile.read forces the NewMail to read when the call occurs, and the reference must be copied to the TEMP variable, which is the perfect solution. But this does not happen in a single thread

The fourth step defines the method to convert the input into the desired event


1 public void Simulatenewmail (string from,string to,string subject) 2         {3             //Constructs an object to hold information to be passed to the notification recipient 4             Newmaileventargs e = new Newmaileventargs (from, to, subject); 5             //Call virtual method notification Object event is reversed 6             //If no type overrides the method 7             // Our object will notify all registered objects of the event 8             onnewmail (e); 9         }

This method indicates that a new message has reached Mailmanager.

1.2 How the compiler implements events

In the Mailmanager class we define the event member itself in a sentence: public event eventhandler<newmaileventargs> NewMail;

The C # compiler translates to the following code:


         A private field that is initialized to null private eventhandler<newmaileventargs> NewMail = null; public void Add_newmail (eventhandler<newmaileventargs> value) {//through loops and calls to CompareExchange, in a thread-safe The way to add a delegate to an event//compareexchange is to compare the target operand (the number of memory pointed to by the 1th parameter)//to a value (3rd parameter), if equal,//to use another value (the 2nd parameter) and the target            The operand (the number in memory pointed to by the 1th parameter) is exchanged eventhandler<newmaileventargs> Prevhandler; Eventhandler<newmaileventargs> NewMail = this.            NewMail;                do {prevhandler = NewMail; Eventhandler<newmaileventargs> Newhandler = (eventhandler<newmaileventargs>) Delegate.Combin                E (Prevhandler, value); NewMail = interlocked.compareexchange<eventhandler<newmaileventargs>> (ref this.            NewMail, Newhandler, Prevhandler);        } while (NewMail! = Prevhandler);  } public void Remove_newmail (eventhandler<newmaileventargs> value)      {eventhandler<newmaileventargs> Prevhandler; Eventhandler<newmaileventargs> NewMail = this.            NewMail;                do {prevhandler = NewMail; Eventhandler<newmaileventargs> Newhandler = (eventhandler<newmaileventargs>) Delegate.Remove                (Prevhandler, value); NewMail = interlocked.compareexchange<eventhandler<newmaileventargs>> (ref this.            NewMail, Newhandler, Prevhandler);        } while (NewMail! = Prevhandler); }

In this example, the Add and remove method accessibility is public because event NewMail is declared public, and the accessibility of the event determines what code can register and unregister the event. However, only the type itself can access the above delegate field NewMail. In addition to the code generated above, the compiler also generates event definition record entries in the metadata of the managed assembly. Contains some flags and the underlying delegate type. The CLR itself does not use these metadata information to run with only accessor methods.

1.3 Designing the types of listening events

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


Internal class Fax    {public        Fax (mailmanager mm)        {            //to Mailmanager NewMail event Register Our callback method            mm. NewMail + = faxmsg;        }        When a new message arrives, Mailmanager calls this method        //sender represents the Mailmanager object, which facilitates passing the information back to it        //e represents the additional event information        that the Mailmanager object wants to pass 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>//deregistration///        </summary>/        <param name= "MM" ></param>        public void Unregister (mailmanager mm)        {            mm. NewMail-= faxmsg;        }    }

When an e-mail application initializes, the Mailmanager object is first constructed and a reference to the object is saved to the variable. The Fax object is then constructed, and the Mailmanager object reference is passed as an argument. In the fax constructor, use + = To register concerns about the NewMail event.

1.4 Implementing an event explicitly

Approximately 70 events are defined for the System.Windows.Forms.Control type. Every object created from a control-derived type wastes a lot of memory, and most of us only care about a few events. How to implement events efficiently by explicitly implementing the class idea that provides a large number of events is as follows:

When defining an event: Each object that exposes the event maintains a collection (such as a dictionary). The collection takes an event identifier as a health, and the delegate list as a value. The collection is also blank when the new object is constructed. Registering a concern for an event finds the identifier for the event in the collection. If the event identifier exists, the new delegate is merged with the delegate list for the event, or the event identifier and delegate are added.

When an event is raised: An object raises an event to look up an identifier for an event in the collection, and if there is no indication that no object is registering attention to the event, then no callback is required. Otherwise, the list of delegates associated with it is called.


 1 public sealed class Eventkey {} 2 public sealed class Eventset 3 {4//define private dictionary 5 private Reado Nly Dictionary<eventkey, delegate> m_events = 6 new Dictionary<eventkey, delegate> (); 7//<summary> 8///non-existent add, presence and existing Eventkey Merge 9//</summary> Public             void Add (Eventkey eventkey,delegate handler) 11 {12//ensure operation only 113 monitor.enter (m_events); 14 Delegate d;15//According to the health gain value of m_events.             TryGetValue (Eventkey, out D); 17//Add or merge M_events[eventkey] = Delegate.combine (d, Handler); 19 Monitor.Exit (m_events);}21///<summary>22//delete delegate, delete the last delegate when deleting the dictionary EVENTKEY-&G         T;DELEGATE23//</summary> public void Remove (Eventkey eventkey,delegate handler) 25 {Monitor.Enter (m_events); Delegate d;28//trygEtvalue ensures that no exception is thrown when attempting to remove a nonexistent eventkey from the collection if (m_events. TryGetValue (eventkey,out D)) {d = delegate.remove (d, Handler);                 LL) 33 {34//If there is a delegate, set a new head m_events[eventkey] = d;36 }37 Else38 {m_events. Remove (Eventkey);}41}42 Monitor.Exit (m_events);}44//< SUMMARY&GT;45////Eventkey event raised for the specified $//</summary> public void Raise (Eventkey even             Tkey,object Sender,eventargs e) (Delegate d;50 monitor.enter (m_events); 51 M_events.                 TryGetValue (Eventkey, out D); Monitor.Exit (m_events); if (D!=null) 54 {55 Using DynamicInvoke, the callback method called to verify the type safety of the parameters, 56//And call methods, if there is a type mismatch, throw exception to the 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//for managing a set of "Events/Delegates" 5 private readonly Eventset m_eventset = new Eventset (); 6//protected properties make derived types can access the collection 7 protected Eventset Eventset {get {return m_eventset;} 8//construct a static read-only object to identify this event 9//each object is Has its own hash code in order to find the delegate list of this event in the collection of objects protected static readonly Eventkey S_fooeventkey = new Eventkey (); 11//define Event accessor methods for adding and removing delegates to the collection eventhandler<fooeventargs> Foo13 {add {m_eventset.ad D (S_fooeventkey, value); }15 Remove {m_eventset.remove (S_fooeventkey, value);} 16}17//define protected virtual methods for this event protected virtual void OnFoo (Fooeventargs e) _eventset.raise (S_fooeventkey, this, e); 21}22//Defines the method to convert the input to this event the public void Simulatefoo () {OnF OO (new Fooeventargs ()); }24} 

How to use typewithlotsofevent, simply register the event with 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)         {11< C10/>console.writeline ("Success");         
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.