SoFunction
Updated on 2025-04-05

In-depth understanding of the design and use of C# events

Related concepts 

Definition: Events are types members used to notify other objects that have a specific thing that has happened to this object.
Note: Events are relatively difficult to understand and practice among .NET type members, because the definition of events is not inherited from the underlying data type, but an encapsulation of delegates. So, before you understand the event, you need to understand a little delegate.
Application scenarios: The application scenarios of events are very wide, and the most common scenario is the design of a large number of trigger event in various front-end controls. The reason is because
Significance: The use of event members is conducive to the implementation of object-oriented principle in the program. For example, the single responsibility principle of type, the control reversal principle. Imagine that if the front-end control cannot abstract a large number of rich events, it will hardly decouple the front-end UI elements from the business logic. The program must be highly coupled.
Application of design patterns: The observer pattern in classic design patterns is implemented very much by relying on the design of event members.
This chapter will parse the design of the event provider and subscriber type by designing a scenario that triggers the event when an email arrives. The case comes from the book "CLR Via C#".

Event Provider Type Design

I. Define types to accommodate all additional information that needs to be sent to event subscribers

Goal: Define a type to pass information to the subscriber of an event
Method: Inherit the default type and implement simple fields, properties and instance constructor members that need to pass information. Examples are as follows:
Copy the codeThe code is as follows:

using System;
using ;

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

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

public string Subject
{
get
{
return ;
}
}

public string To
{
get
{
return ;
}
}

public string From
{
get
{
return ;
}
}
}
}

2. Define event members

Target: Define an event member in the event provider type for registration of event subscriber objects.
Method: Encapsulate a custom delegate to provide a template for event handling methods; or implement a generic type to achieve the same effect. (EventHandler is an encapsulated delegate provided by default). Examples of the two methods are as follows:
Method 1:

Copy the codeThe code is as follows:

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

public class MailManager
{
public event NewMailHandler NewMail;
}

Method 2:
Copy the codeThe code is as follows:

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

Why these two methods can achieve the same effect? ​​Just check the definition to know:
Copy the codeThe code is as follows:

namespace System
{
// Summary:
// Indicates the method to handle events.
//
// Parameters:
// sender:
// Event source.
//
// e:
// A containing event data.
//
// 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 method portal that triggers events to notify the subscription object of the event.

Target: Define a method member in the event provider type to uniformly raise target events.
Note: In order to ensure that this method can only be called in this type and derived types, we need to modify the method to protected. In order for the derived type to override this method, we need to modify the method to virtual
Meaning: The significance of this unified entry method is that it can uniformly maintain the triggering of events and ensure the thread safety of event calls. (Avoid the status of event subscribers is out of sync when triggered by different threads)
Examples are as follows:

Copy the codeThe code is as follows:

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

protected virtual void OnNewMail(NewMailEventArgs e)
{
//For thread safety considerations, now copy the reference to the delegate field into a temporary field
EventHandler<NewMailEventArgs> temp =
(ref NewMail, null, null);

// If there is an event subscriber object, notify them and the event has been fired
if (temp != null)
temp(this, e);
}
}

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

Objective: There is also a business method in the type to convert the scenarios in the business into event triggers. .
Method: In any required business methods, just call the third step method directly, but it is necessary to encapsulate a type that transmits information.
Examples are as follows:

Copy the codeThe code is as follows:

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

protected virtual void OnNewMail(NewMailEventArgs e)
{
//For thread safety considerations, now copy the reference to the delegate field into a temporary field
EventHandler<NewMailEventArgs> temp =
(ref NewMail, null, null);

// If there is an event subscriber object, notify them and the event has been fired
if (temp != null)
temp(this, e);
}

public void SimulateNewMail(string from, string to, string subject)
{
//Construct an object to encapsulate information passed to event subscribers
NewMailEventArgs e = new NewMailEventArgs(from, to, subject);

//The entry method for triggering event trigger
OnNewMail(e);
}
}

Event Subscriber Type Design

I. Define types to subscribe and listen to events

Objective: Design a Fax class to listen for NewMail events.
Note: The Fax type requires a method to subscribe and unsubscribe to NewMail events. Examples are as follows:
Copy the codeThe code is as follows:

internal sealed class Fax
{
private MailManager mailManager;

public Fax(MailManager mm)
{
= mm;
}

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

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

public void Unregister()
{
-= FaxMsg;
}
}