In depth understanding of the design and use of C events

  • 2020-05-19 04:34:15
  • OfStack

Relevant concepts

Definition: an event is a type member that notifies other objects that something specific has happened to this object.
Note: an event is a member of the.NET type that is relatively difficult to understand and practice because the definition of the event is not inherited from the underlying data type, but rather is a encapsulation of the delegate (delegate). So, before you know the event, you need to know 1 point about the delegate.
Application scenarios: there are a wide range of application scenarios for events, among which the most common scenario is the design of a large number of triggering events in various front-end controls. And the reason is because
Meaning: the use of event members facilitates the implementation of object-oriented principles in programs. For example, the single 1 responsibility principle of type, the inversion of control principle. Imagine that if the front controls can't abstract a lot of rich events, it's almost impossible to decouple the front end's UI elements from the business logic. Programs must be highly coupled.
Application of design patterns: the observer pattern in classical design patterns is very dependent on the design of event members.
This chapter resolves the design for the event provider and subscriber types by designing a scenario in which an event is triggered when an E-mail arrives. The case is from the first book CLR Via C#.

Design of event provider types

1. Define the type to accommodate all additional information that needs to be sent to the event subscriber

Target: define 1 type to deliver information to the subscriber of the event
Method: inherits the default System.EventArgs type and implements simple fields, properties, and instance constructor members that need to pass information. Here's an example:

using System; 
using System.Linq; 

namespace ConsoleTest 
{ 
public class NewMailEventArgs : EventArgs 
{ 
private readonly string from, to, subject; 

public NewMailEventArgs(string from, string to, string subject) 
{ 
this.from = from; 
this.to = to; 
this.subject = subject; 
} 

public string Subject 
{ 
get 
{ 
return this.subject; 
} 
} 

public string To 
{ 
get 
{ 
return this.to; 
} 
} 

public string From 
{ 
get 
{ 
return this.from; 
} 
} 
} 
}

2. Define event members

Target: define an event member in the event provider type for the registration of the event subscriber object.
Method: encapsulate a custom delegate to provide a template for event handling. Or implement a generic type of System.EventHandler to achieve the same effect. EventHandler is a default provided encapsulated delegate. Examples of the two approaches are as follows:
Method 1:


public delegate void NewMailHandler(object e, NewMailEventArgs args); 

public class MailManager 
{ 
public event NewMailHandler NewMail; 
}

Method 2:

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

For an idea of why the two methods work the same way, look at the definition of System.EventHandler:

namespace System 
{ 
//  Abstract : 
//  Represents the method by which the event will be handled.  
// 
//  parameter : 
// sender: 
//  The event source.  
// 
// e: 
// 1 Contains event data  System.EventArgs .  
// 
//  Type parameters : 
// TEventArgs: 
//  The type of event data generated by this event.  
[Serializable] 
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e); 
}

3. Define a single method entry to trigger the event to notify the subscription object of the event

Target: defines a method member in the event provider type that is used to raise the target event.
Note: in order to ensure that this method can only be called within this type and derived types, we need to modify the method to protected, and in order for derived types to override this method, we need to modify the method to virtual
Meaning: the purpose of this unified entry method is to maintain the way events are triggered and to ensure thread safety for event calls. (avoid event subscribers' state being out of sync when different threads are firing)
Here's an example:


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

protected virtual void OnNewMail(NewMailEventArgs e) 
{ 
// For thread-safe reasons, now copy the reference to the delegate field into 1 In a temporary field  
EventHandler<NewMailEventArgs> temp = System.Threading.Interlocked.CompareExchange 
(ref NewMail, null, null); 

// If an event subscriber object exists, they are notified that the event has been triggered  
if (temp != null) 
temp(this, e); 
} 
}

4. Among all the business methods that need to trigger events, invoke the method defined in step 3

Target: : there needs to be one more business method in the type to turn a scenario in the business into an event trigger.
Method: in any required business method, simply call the method in step 3, but implement a method that encapsulates a type of message passing.
Here's an example:


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

protected virtual void OnNewMail(NewMailEventArgs e) 
{ 
// For thread-safe reasons, now copy the reference to the delegate field into 1 In a temporary field  
EventHandler<NewMailEventArgs> temp = System.Threading.Interlocked.CompareExchange 
(ref NewMail, null, null); 

// If an event subscriber object exists, they are notified that the event has been triggered  
if (temp != null) 
temp(this, e); 
} 

public void SimulateNewMail(string from, string to, string subject) 
{ 
// structure 1 Object to encapsulate information to the event subscriber  
NewMailEventArgs e = new NewMailEventArgs(from, to, subject); 

// The entry method that triggered the event  
OnNewMail(e); 
} 
}

Design of the event subscriber type

1. Define types to subscribe to and listen for events

Goal: design a fax type Fax class to listen for NewMail events.
Note: the Fax type requires methods to subscribe and unsubscribe to NewMail events. Here's an example:

internal sealed class Fax 
{ 
private MailManager mailManager; 

public Fax(MailManager mm) 
{ 
this.mailManager = mm; 
} 

public void Register() 
{ 
mailManager.NewMail += new EventHandler<NewMailEventArgs>(FaxMsg); 
} 

void FaxMsg(object sender, NewMailEventArgs e) 
{ 
Console.WriteLine("Fax mail message"); 
Console.WriteLine("From = {0}, To = {1}, Subject = {2}", e.From, e.To, e.Subject); 
} 

public void Unregister() 
{ 
mailManager.NewMail -= FaxMsg; 
} 
}

Related articles: