[In-depth explanation of C#] Chapter 5: Advanced Object-Oriented Programming: Delegation and Events

Delegates and events are important concepts in advanced object-oriented programming, which are used to achieve program flexibility, scalability and maintainability. They play a key role in implementing callbacks, event handling, and asynchronous programming, among other things.
Delegates allow us to treat a method as an object that can be passed as a parameter, stored in a variable, and called when needed. This capability makes delegates ideal for implementing callback functions, passing one method to another so that the latter calls the former when appropriate. Delegation also supports the concept of delegation chain and multicast delegation, multiple methods can be linked together to form a delegation chain, and they are executed in sequence.
Events are a special form of delegation used to implement the Observer pattern and event-driven programming. Events provide a concise and reliable way to handle and respond to specific program events, such as user interaction, message notification, and so on. Through the event, we can define the publisher and subscriber of the event. When the publisher triggers the event, the subscriber will receive the notification and perform the corresponding operation. This loosely coupled design pattern makes the program more scalable and maintainable.
Delegates and events also play an important role in asynchronous programming. They can help us handle callbacks and notifications for asynchronous operations, improving the responsiveness and efficiency of programs. By encapsulating asynchronous operations in delegates or events, we can execute corresponding processing logic after asynchronous operations are completed without blocking the main thread or performing complex thread management.

1. The concept and basic use of entrustment

1.1 Definition and characteristics of entrustment

A delegate is a reference type in C# that allows us to treat methods as objects and pass methods as parameters, store them in variables, and call them when needed.
The definition of a delegate includes two main parts: the declaration of the delegate type and the creation of the delegate instance. A delegate type declaration specifies the method's signature, including parameter types and return type. A delegate instance is an object created according to the delegate type, which can refer to one or more methods. The main features of delegation are as follows:

  1. Delegates are type-safe: the delegate type defines the signature of the method, and only methods with the same signature can be assigned to instances of the delegate type.
  2. Delegation is composable: multiple methods can be combined together through delegation chains to form a delegation chain. Each method in the delegation chain can be called in turn.
  3. Delegates are mutable: methods can be added or removed dynamically from the delegate instance. Methods can be added using the "+" operator and removed using the "-" operator.
  4. The delegate is the basis of asynchronous programming: the delegate can be used to handle the callback function of the asynchronous operation, and the corresponding processing is performed by calling the delegate instance after the asynchronous operation is completed.

Delegates play an important role in implementing callbacks, event processing, and multi-threaded programming. They provide a flexible, extensible, and maintainable way to handle method invocation and communication, making programming more flexible and extensible.

1.2 Syntax and declaration of delegate

The syntax and declaration of the delegate mainly include the following steps:

  1. Defining the delegate type: Use delegatethe keyword to define the delegate type. The delegate type defines the signature of the method, including parameter types and return type. The syntax format is as follows:

    delegate <返回类型> <委托类型名>(<参数列表>);
    

    For example, define a delegate type that takes two integer parameters and returns an integer:

    delegate int MyDelegate(int x, int y);
    
  2. Create a delegate instance: Create a delegate instance according to the delegate type, and assign the method to the delegate instance. Delegate instances can be created using anonymous methods, lambda expressions, or named methods. The syntax format is as follows:

    <委托类型> <委托实例名> = new <委托类型>(<方法名>);
    

    For example, create a delegate instance and assign it to a named method:

    MyDelegate myDelegate = new MyDelegate(MyMethod);
    
  3. Call Delegate Instance: Use the delegate instance to call the method. It can be invoked using the delegate instance just like a normal method.

    int result = myDelegate(10, 20);
    

    When calling a delegate instance, the delegate will call these methods in the order of the associated methods and return the result of the last method (if there is a return value).

Precautions:

  • The parameter list and return type of a delegate type must match the signature of the associated method.
  • A delegate instance can only call methods that match the delegate type. A compile error will result if the delegate instance invokes a method that does not match.
  • A delegate type is a reference type, and a reference to a method can be passed through the delegate instance instead of calling the method directly.
  • Methods can be added and removed using the +=and -=operators. +=The operator adds a method to the delegation chain, and -=the operator removes a method from the delegation chain.
1.3 Delegate instantiation and invocation

The instantiation and invocation of the delegate mainly involves the following steps:

  1. Create a delegate instance: Create a delegate instance according to the delegate type and associate it with one or more methods. Delegate instances can be created using anonymous methods, lambda expressions, or named methods.

    <委托类型> <委托实例名> = new <委托类型>(<方法名>);
    

    For example, create a delegate instance and associate it with a named method:

    MyDelegate myDelegate = new MyDelegate(MyMethod);
    
  2. Call the delegate instance: invoke the associated method through the delegate instance. A delegate instance can be called like a normal method, passing parameters and getting a return value.

    <返回值类型> result = <委托实例名>(<参数列表>);
    

    For example, using a delegate instance to call an associated method:

    int result = myDelegate(10, 20);
    

    Note that the invocation of the delegate instance will proceed in the order of the methods in the delegate chain, calling each method in turn, and returning the return value of the last method (if any).

1.4 Delegation Chain and Multicast Delegation

A delegation chain is a mechanism for combining multiple delegation instances into a logical chain. A delegation chain can be created by combining a delegation instance with another delegation instance.
A multicast delegate is a special type of delegate that can contain multiple delegate instances, which are invoked sequentially in the order they are added. By using multicast delegation, delegate instances can be added or removed from the delegate chain, thereby dynamically extending or modifying the behavior of the delegate chain. In C#, you can use +the operator to combine multiple delegate instances into a delegate chain, and use -the operator to remove the delegate instance from the delegate chain.
Here is sample code using multicast delegate:

public delegate void MyDelegate();

static void Main()
{
    
    
    MyDelegate myDelegate1 = Method1;
    MyDelegate myDelegate2 = Method2;

    // 创建委托链
    MyDelegate myDelegateChain = myDelegate1 + myDelegate2;

    // 调用委托链中的方法
    myDelegateChain();

    // 从委托链中移除委托实例
    myDelegateChain -= myDelegate2;

    // 再次调用委托链中的方法
    myDelegateChain();
}

static void Method1()
{
    
    
    Console.WriteLine("Method 1");
}

static void Method2()
{
    
    
    Console.WriteLine("Method 2");
}

Output result:

Method 1
Method 2
Method 1

In the above example, myDelegate1and myDelegate2are two separate delegate instances. By using +the operator to combine them into a delegation chain myDelegateChain, and then when the delegation chain is called, the methods of the two delegate instances will be called in turn. Afterwards, use -the operator to myDelegate2remove from the delegation chain, and when calling the delegation chain again, only myDelegate1the method of will be called.
Multicast delegates provide a convenient and flexible way to handle multiple delegate instances and execute their methods in a specific order. It is very useful in scenarios such as event handling, callback mechanism, etc.

2. Commissioned application scenarios

2.1 Callback function

A common application scenario of delegation is callback function (Callback). The callback function means that when an operation is completed or an event occurs, the system calls a pre-registered function to process the corresponding logic. Through the delegation mechanism, a function can be passed as a parameter to another function, so that the latter can call the incoming function at an appropriate time. This mechanism is useful in situations where asynchronous operations, event handling, user interaction, etc. are required. The following is a sample code that implements a callback function using delegates:

public delegate void CallbackFunction(string message);

public class Operation
{
    
    
    public void LongRunningOperation(CallbackFunction callback)
    {
    
    
        // 模拟耗时操作
        Console.WriteLine("开始执行长时间操作...");
        Thread.Sleep(2000);
        Console.WriteLine("长时间操作完成。");

        // 调用回调函数
        callback("操作已完成");
    }
}

public class Program
{
    
    
    static void Main()
    {
    
    
        Operation operation = new Operation();
        operation.LongRunningOperation(OnOperationComplete);
    }

    static void OnOperationComplete(string message)
    {
    
    
        Console.WriteLine("操作回调:" + message);
    }
}

Output result:

开始执行长时间操作...
长时间操作完成。
操作回调:操作已完成

In the above example, the method Operationin the class LongRunningOperationperforms a time-consuming operation, and then CallbackFunctioncalls the callback function through the passed in delegate type parameter. ProgramThe method in the class OnOperationCompleteacts as a callback function that is called and outputs a message when the operation is complete.
By using delegates and callback functions, the caller can be notified of the result or status of the operation, and the corresponding logic can be executed at an appropriate time, realizing more flexible program control and interaction. Callback functions are often used in scenarios such as asynchronous programming, event-driven programming, and user interface interaction.

2.2 Event processing

Delegates are widely used in event handling. An event refers to a specific action or state change that occurs in a program, and event processing is a mechanism for responding and processing these events. Through the combination of delegation and events, a loosely coupled design pattern can be realized, that is, event-driven programming. In event-driven programming, objects communicate by defining events and corresponding delegates. When an event occurs, the method registered with the corresponding delegate will be called to respond to the event. The following is a sample code for event handling using delegates and events:

public class Button
{
    
    
    public event EventHandler Click;

    public void OnClick()
    {
    
    
        // 触发 Click 事件
        Click?.Invoke(this, EventArgs.Empty);
    }
}

public class Program
{
    
    
    static void Main()
    {
    
    
        Button button = new Button();
        button.Click += Button_Click;

        button.OnClick();
    }

    static void Button_Click(object sender, EventArgs e)
    {
    
    
        Console.WriteLine("按钮被点击了!");
    }
}

Output result:

按钮被点击了!

In the above example, Buttonthe class defines an Clickevent named and uses EventHandlerthe delegate as the type of event handler. ButtonThe method in the class OnClickis used to trigger Clickthe event and Click?.Invoke(this, EventArgs.Empty)call the registered event handler through . In Programthe class, we instantiate an Buttonobject, and register the method as the event handler through +=the operator . Then, the click event of the button is triggered by calling and the corresponding event handler method is executed. By using delegates and events, we can easily decouple events from event processing, making the interaction of objects more flexible and extensible. Event-driven programming patterns are widely used in graphical user interface (GUI), user interaction, asynchronous programming and other scenarios.Button_ClickClickbutton.OnClick()

2.3 Asynchronous programming

Delegate plays an important role in asynchronous programming, it can help handle time-consuming operations and improve application performance and responsiveness. In the traditional synchronous programming model, when a program performs a time-consuming operation, it blocks the main thread, rendering the application unresponsive. The asynchronous programming model implements asynchronous operations by using delegates, so that the main thread can continue to perform other tasks without waiting for the completion of time-consuming operations. The following is a sample code for asynchronous programming using delegates:

public class Worker
{
    
    
    public delegate void WorkCompletedHandler(string result);

    public void DoWorkAsync(WorkCompletedHandler callback)
    {
    
    
        // 模拟耗时操作
        Console.WriteLine("开始执行异步操作...");
        Thread.Sleep(2000);
        string result = "操作已完成";

        // 异步操作完成后调用回调函数
        callback(result);
    }
}

public class Program
{
    
    
    static void Main()
    {
    
    
        Worker worker = new Worker();
        worker.DoWorkAsync(OnWorkCompleted);

        Console.WriteLine("主线程继续执行其他任务...");
        // 等待异步操作完成
        Console.ReadLine();
    }

    static void OnWorkCompleted(string result)
    {
    
    
        Console.WriteLine("异步操作回调:" + result);
    }
}

Output result:

开始执行异步操作...
主线程继续执行其他任务...
异步操作回调:操作已完成

In the above example, the method Workerin the class DoWorkAsyncsimulates a time-consuming asynchronous operation, and WorkCompletedHandlercalls the callback function after the operation is completed through the incoming delegate type parameter. In Programthe class, we instantiate an Workerobject, and call DoWorkAsyncthe method, passing OnWorkCompletedthe method as a callback function. In the main thread, we can continue to perform other tasks without waiting for the completion of the asynchronous operation.
Asynchronous programming can improve the performance and responsiveness of applications through the mechanism of delegation and callback functions. It is widely used in scenarios that need to perform time-consuming operations, avoid main thread blocking, and concurrent processing.

3. The concept and basic use of events

3.1 Definition and characteristics of events

Events are a mechanism in object-oriented programming for handling specific actions or state changes that occur on objects. Events can be thought of as a special type of delegate that provides a loosely coupled way for objects to communicate by defining and triggering events.
Events have the following characteristics:

  1. Publisher and Subscriber Model: Events typically have an object as the publisher that fires the event when certain conditions are met. Other objects can subscribe to the event and provide corresponding processing logic to respond to the occurrence of the event.
  2. Delegates as event handler types: Events often use delegate types to define event handlers. A delegate is a type used to refer to a method, which can be passed as a parameter and invoked when an event occurs.
  3. Registration and deregistration of event handlers: Objects subscribing to events can use +=the operator to register their own methods as event handlers. When an event occurs, the registered event handler will be called. By using -=the operator, an event handler can be unregistered to stop receiving event notifications.
  4. Support for multiple event handlers: Events can support multiple event handlers, that is, multiple methods can subscribe to the same event at the same time. When an event occurs, all subscribed event handlers will be called.
  5. Loosely coupled design: The event mechanism realizes loose coupling between objects, and the publisher object does not need to understand and directly depend on the specific implementation of the subscriber object. Publishers only need to trigger events, and subscribers decide how to handle events.
3.2 Syntax and declaration of events

In C#, the syntax for declaring and using events is as follows:

  1. Define events:
    public event EventHandler MyEvent;
    
    The preceding code defines an MyEventevent named of type EventHandler. EventHandlerIs a predefined delegate type, usually used to handle events without parameters.
  2. Declare event handlers:
    private void OnMyEvent(object sender, EventArgs e)
    {
        // 处理事件的逻辑代码
    }
    
    The above code declares an OnMyEventevent handler method named , which accepts two parameters: senderthe publisher object representing the event, eand the event parameters. According to actual needs, you can customize the name and parameters of the event handler method.
  3. Register event handlers:
    MyEvent += OnMyEvent;
    
    The preceding code OnMyEventregisters the method as MyEventa handler for the event. The method will be called when MyEventthe event fires .OnMyEvent
  4. Unregister event handlers:
    MyEvent -= OnMyEvent;
    
    The above code unregisters OnMyEventthe method from MyEventthe handler list of the event and stops receiving event notifications.

Please note that the above code is just an example, you can adjust and expand it according to your actual needs and scenarios. At the same time, you can also define custom event parameter types as needed to carry more information for the event processor to use.

3.3 Subscription and triggering of events

In C#, the process of subscribing and triggering events is as follows:

  1. Define events:

    public event EventHandler MyEvent;
    

    Define an MyEventevent named , using EventHandlerthe delegate type as the event's type.

  2. Define event handlers:

    private void OnMyEvent(object sender, EventArgs e)
    {
        // 处理事件的逻辑代码
    }
    

    Define a OnMyEventmethod named as an event handler, which accepts two parameters: senderthe publisher object representing the event, eand the event parameter.

  3. Subscribe to events:

    MyEvent += OnMyEvent;
    

    The event handler method is subscribed to the event using +=the operator . This way, when the event fires, the event handler method will be called.OnMyEventMyEventMyEvent

  4. trigger event:

    MyEvent?.Invoke(this, EventArgs.Empty);
    

    ?.InvokeEvents are fired using the syntax MyEvent. This in turn calls all event handler methods subscribed to the event. Parameter thisIndicates the publisher object of the event, and EventArgs.Emptyindicates the event parameters, and an empty parameter object is used here.

  5. Unsubscribe from events:

    MyEvent -= OnMyEvent;
    

    Use -=the operator to unsubscribe an event handler method OnMyEventfrom the event 's subscription list. MyEventThis way, when MyEventthe event fires, the event handler method will no longer be called.

The above are the basic steps of subscribing and triggering events, you can adjust and expand according to actual needs and scenarios. Please note that the subscription and triggering of events should be done at the right time to ensure the correct event handling process.

4. Application Scenarios of Events

4.1 User Interaction in GUI Applications

In GUI (Graphical User Interface) applications, events play an important role in handling user interactions. The following are some common application scenarios of events in GUI applications:

  1. Button click event: an event triggered when the user clicks a button on the interface, and related operations can be performed in the event handler, such as submitting a form, opening a new window, and so on.
  2. Text box input event: An event triggered when the user enters content in the text box. The input text can be obtained through the event handler and processed accordingly, such as input verification, real-time search, etc.
  3. Menu selection event: An event that is triggered when the user selects an option in the menu, and corresponding operations can be performed in the event handler, such as opening a specific function page, executing a specific command, and so on.
  4. Mouse movement and click events: Events triggered when the user moves the mouse on the interface or clicks on a specific element can respond to mouse operations according to the logic of the event handler, such as displaying prompt information, dragging elements, and so on.
  5. Window close event: The event triggered when the user closes the window, you can perform related operations in the event handler, such as saving data, cleaning up resources, etc.

Through the use of events, GUI applications can achieve interaction and response with users, providing a more friendly and flexible user experience. Developers can implement various user interaction logic and functions by subscribing and processing corresponding events.

4.2 Message notification and event-driven

Events have a wide range of application scenarios in message notification and event-driven programming. The following are common use cases for events in these areas:

  1. Message notification: Events can be used to implement a message notification mechanism. When an event occurs, the system can trigger the corresponding event and notify other modules or objects that have subscribed to the event. This can achieve decoupling and message delivery between modules.
  2. Publish-subscribe pattern: Events can be used to implement a publish-subscribe pattern, where one object (publisher) triggers an event, and other objects (subscribers) subscribe to the event and respond to the corresponding processing logic. This pattern is very common in scenarios such as distributed systems and message queues.
  3. User Interaction in GUI Applications: In Graphical User Interface (GUI) applications, event-driven programming is a common pattern. When the user interacts with the interface, events are used to trigger corresponding response operations. For example, clicking a button, dragging an element, keyboard input, etc. can trigger corresponding events for processing.
  4. Asynchronous programming: Events can be used to implement an asynchronous programming model, in which an event is triggered when an operation completes to notify other parts to process. This is useful when dealing with large amounts of data, long-running tasks, or when interaction with external resources is required.
  5. Extension of frameworks and libraries: By defining and using events, developers can provide extension points for frameworks and libraries, allowing other developers to register custom logic on specific events, thereby realizing customized and flexible function extensions.

Through the use of events, loose coupling between modules, flexible scalability and management of asynchronous operations can be achieved. It is a powerful mechanism that enables various parts of a program to work together efficiently and interact in a way that responds to events.

5. Comparison and selection of delegates and events

5.1 The difference between delegate and event

Delegates and events are two important concepts in object-oriented programming, which are used to implement message passing and processing between objects. While they are similar in some respects, there are some differences in their definitions, uses, and purposes.

  1. Definition and syntax:
    • A delegate is a type that encapsulates a reference to a method. It defines the method's signature and return type, and can be used to declare variables, parameters, and return types.
    • Events are a special type of delegate used to define and trigger specific actions. Events are eventdeclared using the keyword and can only be defined in classes or structures.
  2. Role and purpose:
    • Delegates are used to pass references to methods, making it possible to pass methods as parameters to other methods or store them in variables. Delegates are often used in scenarios such as callback functions, event processing, and asynchronous programming.
    • Events are a special type of delegate used to define and trigger specific actions or notifications. It allows a class or structure to notify other objects when a specific event occurs and execute the corresponding event handler.
  3. Subscribe and trigger:
    • A delegate can +=subscribe to multiple methods by using the operator, so that multiple methods can respond to the call of the delegate. When the delegate is invoked, the subscribed methods will be invoked in turn.
    • An event is a special form of delegation, which is only allowed to be triggered inside the class, and external objects can only respond to the occurrence of events by subscribing to the event.
  4. Security and encapsulation:
    • Events have higher security and encapsulation, because events can only be triggered inside the class, and external objects cannot directly call or change the triggering of events.
    • A delegate is relatively more flexible in use because it can be stored in a variable and allows external objects to call the delegate directly.
5.2 Select the appropriate delegate and event

When choosing suitable delegates and events, specific application scenarios and requirements need to be considered. Here are some suggestions:

  1. commissioned:
    • Use delegates to pass method references to meet requirements such as callback functions or asynchronous programming.
    • If you need to pass methods between different objects, and you want these objects to be able to make method calls independently, you can choose to use delegation.
  2. event:
    • Use events to define and trigger specific actions or notifications to achieve decoupling and message passing between objects.
    • If you need to trigger a specific action inside the class and want other objects to subscribe and respond to this action, you can choose to use events.
  3. Consider security and encapsulation:
    • If you want to limit the triggering and operation of events by external objects and protect the internal state of the class, you can choose to use events.
    • If you need to flexibly pass the reference of the method, and you want the external object to call the delegate directly, you can choose to use the delegate.
  4. Consider scalability and reusability:
    • You can choose to use events when you want to be able to share the same event definition among multiple classes and have each class be able to add and respond to event handlers independently.
    • If you want to be able to reuse the same delegate type in multiple places and not be limited to internal events of a specific class, you can choose to use delegates.

In short, delegates are suitable for scenarios such as passing method references, implementing callback functions, and asynchronous programming, while events are suitable for defining and triggering specific actions or notifications, and achieving decoupling between objects. According to the requirements of the application, choose the most appropriate mechanism to realize the function and meet the requirements.

6. Best Practices and Precautions for Commissioning and Events

Here are some best practices and considerations when using delegates and events:

  1. Naming of delegates and events: Naming should accurately reflect their purpose and function, and follow naming conventions to improve code readability.
  2. Delegated lifecycle management: When using delegates, you need to ensure that the delegated lifecycle is managed correctly to avoid potential memory leaks. Use the appropriate methods to add and remove delegated subscriptions.
  3. Event triggering timing: When designing and implementing events, you need to consider the triggering timing of events to ensure that events are triggered at an appropriate time to meet requirements and functions.
  4. Security of event handlers: When other objects subscribe and respond to events, the security of event handlers needs to be ensured to handle possible exceptions and error conditions to ensure program stability.
  5. Documentation of delegates and events: Provide clear documentation in the code to explain the purpose, usage, and expected behavior of delegates and events to help other developers understand and use them.
  6. Applicability of delegates and events: When choosing to use delegates and events, specific needs and scenarios need to be considered to ensure their applicability and rationality. Don't abuse delegates and events, but choose the appropriate programming mechanism according to the actual situation.
  7. Code clarity and maintainability: When using delegates and events, keep code clarity and maintainability, and follow good coding style and design principles to improve code readability and maintainability.

7. Summary

Delegates and events are important concepts in object-oriented programming, they provide flexibility and extensibility, enabling us to achieve decoupled and reusable code. Delegates allow us to pass and store methods as parameters and call them when needed, which is very useful for implementing callback functions and asynchronous programming. Events are a special form of delegation that handle specific actions or trigger specific conditions. Events provide a loosely coupled way to notify and respond to interactions between objects.
When using delegates and events, we should follow best practices and considerations, such as accurate naming, correct lifecycle management, timely triggering of events, handling security and exceptions, providing clear documentation, etc. Choosing the appropriate delegate and event depends on the specific needs and scenarios, and ensure its applicability and rationality. It is very important to keep the code clear and maintainable. Good coding style and design principles can improve the readability and maintainability of the code.

Guess you like

Origin blog.csdn.net/gangzhucoll/article/details/131750446