Tutorialsteacher

关注我们

文章
  • C#
  • C# 面向对象编程
  • ASP.NET Core
  • ASP.NET MVC
  • LINQ
  • 控制反转 (IoC)
  • Web API
  • JavaScript
  • TypeScript
  • jQuery
  • Angular 11
  • Node.js
  • D3.js
  • Sass
  • Python
  • Go 语言
  • HTTPS (SSL)
  • 正则表达式
  • SQL
  • SQL Server
  • PostgreSQL
  • MongoDB
  • C# - 入门
  • C# - 版本历史
  • C# - 第一个程序
  • C# - 关键词
  • C# - 类和对象
  • C# - 命名空间
  • C# - 变量
  • C# - 隐式类型变量
  • C# - 数据类型
  • 数字
  • 字符串
  • DateTime
  • 结构体
  • 枚举
  • StringBuilder
  • 匿名类型
  • 动态类型
  • 可空类型
  • C# - 值类型和引用类型
  • C# - 接口
  • C# - 运算符
  • C# - if else 语句
  • C# - 三元运算符 ?
  • C# - Switch 语句
  • C# - For 循环
  • C# - While 循环
  • C# - Do-while 循环
  • C# - 分部类
  • C# - Static 关键字
  • C# - 数组
  • 多维数组
  • 交错数组
  • C# - 索引器
  • C# - 泛型
  • 泛型约束
  • C# - 集合
  • ArrayList
  • List
  • SortedList
  • Dictionary
  • Hashtable
  • Stack
  • Queue
  • C# - 元组 (Tuple)
  • C# - 值元组 (ValueTuple)
  • C# - 内置异常
  • 异常处理
  • throw 关键字
  • 自定义异常
  • C# - 委托
  • Func 委托
  • Action 委托
  • Predicate 委托
  • 匿名方法
  • C# - 事件
  • C# - 协变
  • C# - 扩展方法
  • C# - 流 I/O
  • C# - File 类
  • C# - FileInfo 类
  • C# - 对象初始化器
  • OOP - 概述
  • 面向对象编程
  • 抽象
  • 封装
  • 关联与组合
  • 继承
  • 多态
  • 方法重写
  • 方法隐藏
  • C# - SOLID 原则
  • 单一职责原则
  • 开闭原则
  • 里氏替换原则
  • 接口隔离原则
  • 依赖倒置原则
  • 设计模式
  • 单例模式
  • 抽象工厂模式
  • 工厂方法模式
Entity Framework Extensions - 提升 EF Core 9
  批量插入
  批量删除
  批量更新
  批量合并

C# - 事件

事件是对象发出的通知,用于表示某个操作的发生。C# 中的事件遵循观察者设计模式。

引发事件的类称为发布者,接收通知的类称为订阅者。单个事件可以有多个订阅者。通常,当某个操作发生时,发布者会引发一个事件。对接收操作发生通知感兴趣的订阅者应该注册事件并处理它。

在 C# 中,事件是委托的封装。它依赖于委托。委托定义了订阅者类的事件处理方法的签名。

下图说明了 C# 中的事件。

Event Publisher & Subscriber

声明事件

事件可以分两步声明

  1. 声明一个委托。
  2. 使用 `event` 关键字声明委托的一个变量。

以下示例演示了如何在发布者类中声明事件。

示例:声明事件
public delegate void Notify();  // delegate
                    
public class ProcessBusinessLogic
{
    public event Notify ProcessCompleted; // event

}

在上面的示例中,我们声明了一个委托 `Notify`,然后在使用 `ProcessBusinessLogic` 类中的 "event" 关键字声明了一个 `Notify` 委托类型的事件 `ProcessCompleted`。因此,`ProcessBusinessLogic` 类被称为发布者。`Notify` 委托指定了 `ProcessCompleted` 事件处理程序的签名。它指定订阅者类中的事件处理方法必须具有 void 返回类型且没有参数。

现在,让我们看看如何引发 `ProcessCompleted` 事件。考虑以下实现。

示例:引发事件
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
    {
        //if ProcessCompleted is not null then call delegate
        ProcessCompleted?.Invoke(); 
    }
}

在上面,`StartProcess()` 方法在末尾调用了 `onProcessCompleted()` 方法,该方法引发了一个事件。通常,为了引发事件,应该定义一个受保护的虚方法,名称为 `On`。受保护和虚方法使派生类能够覆盖引发事件的逻辑。但是,派生类应始终调用基类的 `On` 方法,以确保注册的委托接收到事件。

`OnProcessCompleted()` 方法使用 `ProcessCompleted?.Invoke();` 调用委托。这将调用所有注册到 `ProcessCompleted` 事件的事件处理方法。

订阅者类必须注册到 `ProcessCompleted` 事件并使用其签名与 `Notify` 委托匹配的方法处理它,如下所示。

示例:消费事件
class Program
{
    public static void Main()
    {
        ProcessBusinessLogic bl = new ProcessBusinessLogic();
        bl.ProcessCompleted += bl_ProcessCompleted; // register with an event
        bl.StartProcess();
    }

    // event handler
    public static void bl_ProcessCompleted()
    {
        Console.WriteLine("Process Completed!");
    }
}
尝试一下

在上面,`Program` 类是 `ProcessCompleted` 事件的订阅者。它使用 += 运算符注册事件。请记住,这是我们向多播委托的调用列表中添加方法的相同方式。`bl_ProcessCompleted()` 方法处理事件,因为它与 `Notify` 委托的签名匹配。

内置 EventHandler 委托

.NET Framework 包含内置委托类型 EventHandler 和 EventHandler<TEventArgs>,用于最常见的事件。通常,任何事件都应包含两个参数:事件源和事件数据。对于不包含事件数据的所有事件,请使用 `EventHandler` 委托。对于包含要发送给处理程序的数据的事件,请使用 EventHandler<TEventArgs> 委托。

上面显示的示例可以使用 `EventHandler` 委托而无需声明自定义 `Notify` 委托,如下所示。

示例:EventHandler
class Program
{
    public static void Main()
    {
        ProcessBusinessLogic bl = new ProcessBusinessLogic();
        bl.ProcessCompleted += bl_ProcessCompleted; // register with an event
        bl.StartProcess();
    }

    // event handler
    public static void bl_ProcessCompleted(object sender, EventArgs e)
    {
        Console.WriteLine("Process Completed!");
    }
}

public class ProcessBusinessLogic
{
    // declaring an event 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);
    }
}
尝试一下

在上面的示例中,事件处理程序 `bl_ProcessCompleted()` 方法包含两个与 EventHandler 委托匹配的参数。此外,当我们使用 `OnProcessCompleted()` 方法中的 `Invoke()` 引发事件时,将 `this` 作为发送者和 `EventArgs.Empty` 传递。因为我们的事件不需要任何数据,它只是通知订阅者进程完成,所以我们传递了 `EventArgs.Empty`。

传递事件数据

大多数事件都会向订阅者发送一些数据。EventArgs 类是所有事件数据类的基类。.NET 包含许多内置事件数据类,例如 SerialDataReceivedEventArgs。它遵循所有事件数据类以 EventArgs 结尾的命名模式。您可以通过派生 EventArgs 类来创建自定义事件数据类。

使用 EventHandler<TEventArgs> 将数据传递给处理程序,如下所示。

示例:传递事件数据
class Program
{
    public static void Main()
    {
        ProcessBusinessLogic bl = new ProcessBusinessLogic();
        bl.ProcessCompleted += bl_ProcessCompleted; // register with an event
        bl.StartProcess();
    }

    // event handler
    public static void bl_ProcessCompleted(object sender, bool IsSuccessful)
    {
        Console.WriteLine("Process " + (IsSuccessful? "Completed Successfully": "failed"));
    }
}

public class ProcessBusinessLogic
{
    // declaring an event 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);
    }
}
尝试一下

在上面的示例中,我们向处理程序传递一个布尔值,表示进程是否成功完成。

如果要传递多个值作为事件数据,则创建一个派生自 EventArgs 基类的类,如下所示。

示例:自定义 EventArgs 类
class ProcessEventArgs : EventArgs
{
    public bool IsSuccessful { get; set; }
    public DateTime CompletionTime { get; set; }
}

以下示例演示了如何将自定义 `ProcessEventArgs` 类传递给处理程序。

示例:传递自定义 EventArgs
class Program
{
    public static void Main()
    {
        ProcessBusinessLogic bl = new ProcessBusinessLogic();
        bl.ProcessCompleted += bl_ProcessCompleted; // register with an event
        bl.StartProcess();
    }

    // event handler
    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
{
    // declaring an event 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);
    }
}
尝试一下

因此,您可以在 C# 中创建、引发、注册和处理事件。

了解 C# 中委托和事件的区别。

要点
  1. 事件是委托的包装器。它依赖于委托。
  2. 使用“event”关键字与委托类型变量一起声明事件。
  3. 对于常见事件,使用内置委托 EventHandler 或 EventHandler<TEventArgs>。
  4. 发布者类引发事件,订阅者类注册事件并提供事件处理方法。
  5. 将引发事件的方法命名为以“On”前缀加上事件名称。
  6. 处理程序方法的签名必须与委托签名匹配。
  7. 使用 += 运算符注册事件。使用 -= 运算符取消订阅。不能使用 = 运算符。
  8. 使用 EventHandler<TEventArgs> 传递事件数据。
  9. 派生 EventArgs 基类以创建自定义事件数据类。
  10. 事件可以声明为静态、虚拟、密封和抽象。
  11. 接口可以包含事件作为成员。
  12. 如果有多个订阅者,事件处理程序将同步调用。
TUTORIALSTEACHER.COM

TutorialsTeacher.com 是您权威的技术教程来源,旨在通过循序渐进的方法,指导您掌握各种网络和其他技术。

我们的内容旨在帮助所有级别的学习者轻松快速地学习技术。通过访问本平台,您承认您已审阅并同意遵守我们的使用条款和隐私政策,这些条款和政策旨在保护您的体验和隐私权。

[email protected]

关于我们使用条款隐私政策
copywrite-symbol

2024 TutorialsTeacher.com. (v 1.2) 版权所有。