English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
C# Object-Oriented (OOP)
Events are notifications sent by objects to represent the occurrence of an operation. .NET events follow the observer design pattern. The class that triggers an event is called
Publisher (publisher), the class that receives notifications is called Subscriber (subscriber). An event can have multiple subscribers. Usually, the publisher triggers an event when an operation occurs. Subscribers who want to be notified when an operation occurs should register with the event and handle it.
In C#, events are encapsulated delegates. They depend on delegates. Delegates define the signature of the event handler method for the subscriber class.
Using delegates to publish events Publisher(publisher) Class. Other classes that accept this event are called subscriber(subscriber) classes. Events are declared and generated in the class and associated with event handlers through delegates in the same class or other classes. The class that contains events is used to publish events. This is called Publish-Subscribe(publisher-subscriber) Model.
Publisher(publisher)- It is an object that contains event and delegate definitions. The relationship between events and delegates is also defined in this object. The object of the publisher class calls this event and notifies other objects.
Subscriber (subscriber)- Is an object that accepts events and provides event handlers. The delegate in the publisher (publisher) class calls the method (event handler) in the subscriber (subscriber) class.
An event can be declared in two steps:
Declare a delegate
Declare a variable using the event keyword
The following example demonstrates how to declare an event in the publisher class.
public delegate void Notify(); // Delegate public class ProcessBusinessLogic { public event Notify ProcessCompleted; // Event }
In the above example, we declared a delegate Notify and then declared an event of delegate type Notify named ProcessCompleted in the ProcessBusinessLogic class. Therefore, the ProcessBusinessLogic class is called the publisher (publisher). The Notify delegate specifies the signature of the event handler for the ProcessCompleted event. It specifies that the event handler method in the subscriber (subscriber) class must have a void return type and no parameters.
Now, let's see how to raise the ProcessCompleted event. See the following implementation.
public delegate void Notify(); // Delegate public class ProcessBusinessLogic { public event Notify ProcessCompleted; // Event public void StartProcess() { Console.WriteLine("Process Started!"); // Some code here... OnProcessCompleted(); } protected virtual void OnProcessCompleted() //Protected virtual method { //Invoke the delegate if ProcessCompleted is not null. ProcessCompleted?.Invoke(); } }
Above, the StartProcess() method calls the onProcessCompleted() method at the end, which will trigger an event. Typically, to raise an event, a protected and virtual method with the name defined on <EventName> should be used. Protected and virtual allow derived classes to override the logic that raises the event. However, derived classes should always call the base class's On<EventName> method to ensure that the registered delegate receives the event.
The OnProcessCompleted() method invokes the delegate using ProcessCompleted?.Invoke(). This will call all event handler methods registered to the ProcessCompleted event.
The subscriber class must register to the ProcessCompleted event and handle it using a method that matches the signature of the Notify delegate, as shown below.
class Program { public static void Main() { ProcessBusinessLogic bl = new ProcessBusinessLogic(); bl.ProcessCompleted += bl_ProcessCompleted; // Register events bl.StartProcess(); } // Event handler public static void bl_ProcessCompleted() { Console.WriteLine("Process Completed!"); } }
Above, the Program class is ProcessCompleted Event subscribers. It uses + = operator for event registration. Remember, this is the same as adding a method to the call list of a multicast delegate. The bl_processcompleted() method handles the event because it matches the signature of the Notify delegate.
.NET Framework includes built-in delegate types EventHandler and EventHandler<TEventArgs> for the most common events. Typically, any event should include two parameters: the event source and the event data. For all events that do not contain event data, use the EventHandler delegate. For events that need to send data to the handler, use the EventHandler<TEventArgs> delegate.
The example shown above can use the EventHandler delegate without declaring a custom Notify delegate as follows.
class Program { public static void Main() { ProcessBusinessLogic bl = new ProcessBusinessLogic(); bl.ProcessCompleted += bl_ProcessCompleted; // Event registration bl.StartProcess(); } // Event handling public static void bl_ProcessCompleted(object sender, EventArgs e) { Console.WriteLine("Process Completed!"); } } public class ProcessBusinessLogic { // Declare events using built-in EventHandler public event EventHandler ProcessCompleted; public void StartProcess() { Console.WriteLine("Process Started!"); // Some code here... OnProcessCompleted(EventArgs.Empty); //No event data } protected virtual void OnProcessCompleted(EventArgs e) { ProcessCompleted?.Invoke(this, e); } }
In the above example, the bl_ProcessCompleted() method contains two parameters that match the EventHandler delegate. At the same time, this is passed as the sender and EventArgs. When we use the Invoke() method to raise the event in the OnProcessCompleted() method, it is empty. Because our event does not require any data, it just notifies subscribers that the process has been completed, so we passed EventArgs.Empty.
Most events send some data to subscribers. The EventArgs class is the base class for all event data classes. .NET includes many built-in event data classes, such as SerialDataReceivedEventArgs. It follows the naming pattern of ending all event data classes with EventArgs. You can create custom classes for event data by deriving from the EventArgs class.
Pass data to the handler using EventHandler<TEventArgs>, as shown below.
class Program { public static void Main() { ProcessBusinessLogic bl = new ProcessBusinessLogic(); bl.ProcessCompleted += bl_ProcessCompleted; // Event registration bl.StartProcess(); } // Event handling public static void bl_ProcessCompleted(object sender, bool IsSuccessful) { Console.WriteLine("Process " + (IsSuccessful ? "Completed Successfully" : "failed"); } } public class ProcessBusinessLogic { // Declare events using built-in EventHandler public event EventHandler<bool> ProcessCompleted; public void StartProcess() { try { Console.WriteLine("Process Started!"); // Some code here... OnProcessCompleted(true); } catch(Exception ex) { OnProcessCompleted(false); } } protected virtual void OnProcessCompleted(bool IsSuccessful) { ProcessCompleted?.Invoke(this, IsSuccessful); } }
In the above example, we pass a single boolean value to the handler to indicate whether the process has completed successfully.
If you want to pass multiple values as event data, you can create a class derived from the EventArgs base class, as shown below.
class ProcessEventArgs : EventArgs { public bool IsSuccessful { get; set; } public DateTime CompletionTime { get; set; } }
The following example demonstrates how to pass a custom ProcessEventArgs class to the handler.
class Program { public static void Main() { ProcessBusinessLogic bl = new ProcessBusinessLogic(); bl.ProcessCompleted += bl_ProcessCompleted; // Event registration bl.StartProcess(); } // Event handling public static void bl_ProcessCompleted(object sender, ProcessEventArgs e) { Console.WriteLine("Process " + (e.IsSuccessful ? "Completed Successfully" : "failed")); Console.WriteLine("Completion Time: " + e.CompletionTime.ToLongDateString()); } } public class ProcessBusinessLogic { // Declare events using built-in EventHandler public event EventHandler<ProcessEventArgs> ProcessCompleted; public void StartProcess() { var data = new ProcessEventArgs(); try { Console.WriteLine("Process Started!"); // Some code here... data.IsSuccessful = true; data.CompletionTime = DateTime.Now; OnProcessCompleted(data); } catch(Exception ex) { data.IsSuccessful = false; data.CompletionTime = DateTime.Now; OnProcessCompleted(data); } } protected virtual void OnProcessCompleted(ProcessEventArgs e) { ProcessCompleted?.Invoke(this, e); } }
Therefore, you can create, trigger, register, and handle events in C#.
An event is a wrapper around a delegate. It depends on the delegate.
Use the 'event' keyword along with a delegate type variable to declare an event.
Built-in delegateEventHandler orEventHandler <TEventArgs> is used for common events.
The publisher class triggers an event, while the subscriber class registers an event and provides an event handler method.
Name the method that raises the event with the event name, prefixed with 'On'.
The signature of the handler method must match the delegate signature.
Use+ The = operator registers events. Use -The = operator unsubscribes; the = operator cannot be used.
Pass event data using EventHandler <TEventArgs>.
Derive from the EventArgs base class to create a custom event data class.
Events can be declared as static, virtual, sealed, and abstract (static, virtual, sealed, abstract).
An interface can include events as members.
If there are multiple subscribers, the event handler will be called synchronously.