C # event analysis,

Source: Internet
Author: User

C # event analysis,

Preface

For. net friends, they often encounter problems about events and delegation: What is the relationship between events and delegation? What is the essence of an event? What is the essence of delegation? Because. net has made a lot of encapsulation, these two concepts are not very well understood for beginners. Events are the basis for interaction between users and applications. They are an application of the callback mechanism. For example, when a user clicks a button, we want to pop up a "hello"; here "click" is an event; then, callback is the method we register, when a user clicks, the program automatically executes this method to respond to this operation, instead of monitoring whether the user clicks.

The previous article has introduced the delegation-related knowledge, which is. net is used to implement the callback mechanism, and events are an application of the callback mechanism. Before learning events, you should first learn the delegated knowledge. With the foundation of delegation, let's proceed step by step into the event.

I. Use Events

Assume that there is an email administrator (event owner) who assigns a value to receive the email. When the email arrives (event), the email can be sent to the fax staff and print staff for processing (event subscriber ). Obviously, only the email administrator knows the arrival time, and the fax and print members cannot always ask for new emails. Instead, the administrator should take the initiative to notify (callback ), but they also need to tell the Administrator that I need to process (subscribe) The new mail when it comes ). Receiving a new email is an event. The email has certain information (additional event information), such as the sender, recipient, and content. Next we will learn about the event through this process.

1. define additional information required for an event

According to the Convention, the additional information of all events should be derived from EventArgs, because we want to know that this is an additional information parameter of the event, rather than other parameters. EventArgs is defined as follows:

public class EventArgs {    public static readonly EventArgs Empty = new EventArgs();    public EventArgs() {}}

We can see that this class has a static read-only field Empty, which is a singleton; it corresponds to String. like Empty, when we need an Empty EventArgs, we should use EventArgs. empty, instead of re-entering a new one.

We define a NewMailEventArgs parameter, as follows:

class NewMailEventArgs : EventArgs{    private string from;    private string to;    private string content;     public string From { get { return from; } }    public string To { get { return to; } }    public string Content { get { return content; } }     public NewMailEventArgs(string from,string to,string content)    {        this.from = from;        this.to = to;        this.content = content;    }}

2. Define events

The event keyword is used for defining events in c #, and events are generally public. We define a NewMail event as follows:

public event EventHandler<NewMailEventArgs> NewMail;

NewMail is an event, but. net does not. In fact, here it is an EventHandler <TEventArgs> delegate (the delegate is a reference type), but it is modified with the event. It can also be said that it is an event-type delegate.

We know that the delegate is used to wrap the callback function, and its essence is a class. The signature of the callback function must be consistent with that of the delegate. An event can have multiple processing functions. A function is encapsulated into a delegate object, and all the delegate objects are saved in the NewMail delegate chain. Therefore, triggering a NewMail event is actually traversing the delegate chain of the delegate object to which it points, and executing the method encapsulated by each delegate object. (You can check the delegation if you are not clear about it)

EventHandler is a generic delegate. Its definition is as follows:

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

Here there are two features: 1. the return value is void, because event processing functions generally do not need to return values. Most of the event processing functions in. net do not return values. 2. the sender parameter of the object type, which indicates the event owner. This is also logical. In addition to the actual additional information, we also need to know where the event came from; the reason why the object type is used is that it can be used for more types.

3. Define event-triggered Functions 

// 3. Define the event trigger method protected virtual void OnNewMail (NewMailEventArgs e) {/* 1st if (this. NewMail! = Null) {this. NewMail (this, e);} * // * method 2 EventHandler <NewMailEventArgs> temp = this. NewMail; if (temp! = Null) {temp (this, e);} * // method 3: EventHandler <NewMailEventArgs> temp = Interlocked. compareExchange (ref this. newMail, null, null); if (temp! = Null) {temp (this, e );}}

The first approach is a common practice. If it is determined that it is not empty, it is triggered. The CLR mentioned that this is a thread unsafe practice, because when we judge that it is not empty, another thread will remove the delegate from the delegate chain when it is ready to be executed, an NullReferenceException is thrown. The second and third methods are thread-safe, because it uses a temporary delegate variable (the delegate chain stores all the delegates) and the previous article's understanding of the delegate chain, we know that the Combine/Remove operation on the delegate chain will actually create a new array object, which has no effect on temp. But in fact, events are mainly used in a single-threaded environment, so this problem is generally not encountered.

4. Wrap the event parameters and call the event-triggered function.

public void ReceiveNewMail(string from, string to, string content){    NewMailEventArgs e = new NewMailEventArgs(from, to, content);    OnNewMail(e);}

Next, if you are interested in this event, you can register it.

class Fax{    public Fax(MailManager mm)    {        mm.NewMail += FaxMsg;    }     private void FaxMsg(object sender, NewMailEventArgs e)    {        Console.WriteLine(string.Format("fax receive,from:{0} to:{1} content is:{2}", e.From, e.To, e.Content));    }}

Ii. event exposure

We have mentioned that the essence of an event is delegation, or an application of delegation. To learn more about the event, we can use ildasm.exe to find the code generated by defining the event.

public event EventHandler<NewMailEventArgs> NewMail;  

We can see that when we define a NewEvent, the compiler generates: 1. A private NewMail field with the type of EventHandler <NewMailEventArgs>. 2. An add_NewMail method is used to add the Delegate to the Delegate chain (the Delegate. Combine method is called internally ). 3. A remove_NewMail method is used to Remove the Delegate from the Delegate chain (the Delegate. Remove method is called internally ). The operation on the event is the operation on the NewMail field.

Now we know,An event is essentially a delegate, and an event is a delegate.The compiler hides this process. Why not directly use the delegate?

Iii. Why not use delegation directly

We know that the essence of an event is delegation, where the event is implemented, it can also be implemented by delegation. The above code can be written in this way to achieve the same purpose:

class MailManager{    public EventHandler<EventArgs> NewMail;            public void RaiseNewMail()    {        if (NewMail != null)        {            NewMail(this, EventArgs.Empty);        }    }}

External call:

Class Fax {public Fax (MailManager mm) {mm. newMail + = FaxNewMail;} public void FaxNewMail (object sender, EventArgs e) {Console. writeLine ("Fax New Mail processing succeeded ");}}

We often do not design the field for maintaining the object state as the public type, because the external field can be changed at will, which is not what we want to see. For example, we can directly call the NewMail Invoke method externally. In addition, we cannot control the specific process of obtaining and setting fields. To control the fields, we need to define a Get _ method and a Set _ method. For the delegate type, add _ and Remove _. It is very troublesome to define Add _/Remove _ for each event. Speaking of this, we will immediately think of the property design. Yes, the property uses the Get _/Set _ method to provide access to private fields (Non-Commissioned, the Add _/Remove _ method is used to access the private delegate field.

4. display implementation events

We know that when implicit implementation of attributes, the compiler will generate a private field for us, for example, public string Name {get; set;} will automatically generate a _ name field. But when we show the implementation, the compiler will not make a living. For example:

public string Name{    get{return "Tom";}    set{}}

For events, the display implementation is:

private EventHandler<NewMailEventArgs> _newMail;public event EventHandler<NewMailEventArgs> NewMail{    add    {        _newMail += value;    }    remove    {        _newMail -= value;    }}

EventHandlerList of Control

For Control, it defines a large number of events (defined events are defined as delegates), and these events are not necessarily used, so this will waste a lot of memory. Therefore, all the events in the Control are displayed and the delegate is saved in an EventHandlerList set. This is a set of key-value. In this way, you only need to add the corresponding delegate to handle the events, which looks like this:

class Control{    private EventHandlerList events;    protected EventHandlerList Events    {        get        {            if (this.events == null)            {                this.events = new EventHandlerList();            }            return this.events;        }    }     private static readonly object _clickEventObj = new object();    private static readonly object _mouseOverEventObj = new object();            public event EventHandler<EventArgs> Click    {        add        {            this.Events.AddHandler(_clickEventObj, value);        }        remove        {            this.Events.RemoveHandler(_clickEventObj, value);        }    }     public event EventHandler<EventArgs> MouseOver    {        add        {            this.Events.AddHandler(_mouseOverEventObj, value);        }        remove        {            this.Events.RemoveHandler(_mouseOverEventObj, value);        }    }}

That is, we define an object type for each event as the key of the set. Although many objects are defined, the cost of the object is much lower than that of the delegate.

At this point, we should know that the essence of the delegate is the reference type, which is used to wrap the callback function and the delegate is used to implement the callback mechanism. The essence of the event is the delegate, and the event is an application of the callback mechanism.

The above motion graphs are provided by "Graph dado ".

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.