• C#_事件简述


    事件模型简述

    C#中事件的运行模式为"发布订阅模型"事件触发者称为"发布者",事件处理者称为"订阅者"

    事件模型的五个组成部分

    1. 事件(成员)
    2. 事件的拥有者(类/对象)
    3. 事件的响应者(类/对象)
    4. 事件处理器(事件处理器的成员方法)
    5. 事件订阅(+= -=)

    五个组成部分的关系为:

    事件的拥有者拥有事件事件的响应者订阅事件。当事件被触发后,事件的拥有者事件通知事件的响应者事件的响应者通过事件处理器处理事件

    事件示例:System.Timers.Timer

    System.Timers.Timer是.NET提供的线程不安全的计时器类,此处介绍其Elapsed事件

    1. System.Timers.Timer timer = new(1000);
    2. // Elapsed事件 通过"+="订阅事件
    3. timer.Elapsed += (sender, e) =>
    4. {
    5. Console.WriteLine("System.Timers.Timer");
    6. };
    7. timer.Start();
    8. Thread.Sleep(6000);
    9. // 停止计时器
    10. timer.Dispose();

    间隔1S事件就会被触发一次,然后被事件处理器处理

    事件的完整声明

    1. private static void Main(string[] args)
    2. {
    3. Customer customer = new();
    4. Waiter waiter = new();
    5. customer.Order += waiter.TakeOrder;
    6. customer.Think("Cake", "Medium");
    7. }
    8. public class Customer
    9. {
    10. private OrderEventHandler orderEventHandler; // 事件用于接收订阅的委托
    11. public event OrderEventHandler Order // 事件
    12. {
    13. add
    14. {
    15. orderEventHandler += value;
    16. }
    17. remove
    18. {
    19. orderEventHandler -= value;
    20. }
    21. }
    22. public double Bill { get; set; }
    23. public void Think(string dishName, string size)
    24. {
    25. Console.WriteLine("Customer: I need {0} {1}", size, dishName);
    26. OnOrder("Cake", "Medium");
    27. }
    28. protected void OnOrder(string dishName, string size)
    29. {
    30. if (orderEventHandler != null)
    31. {
    32. OrderEventArgs args = new();
    33. args.DishName = dishName;
    34. args.Size = size;
    35. orderEventHandler(this, args);
    36. }
    37. }
    38. }
    39. public class Waiter
    40. {
    41. public void TakeOrder(Customer customer, OrderEventArgs e)
    42. {
    43. Console.WriteLine("I will serve you the dish - {0} {1}", e.Size, e.DishName);
    44. double basePrice = 10;
    45. switch (e.Size)
    46. {
    47. case "small":
    48. basePrice *= 0.5;
    49. break;
    50. case "large":
    51. basePrice *= 1.5;
    52. break;
    53. default:
    54. break;
    55. }
    56. customer.Bill += basePrice;
    57. Console.WriteLine("You need to pay ${0}", customer.Bill);
    58. }
    59. }
    60. // 依据.net规范, 类的作用是传递事件信息(EventArgs)时, 需在声明时添加EventArgs后缀, 并实现EventArgs类
    61. public class OrderEventArgs : EventArgs
    62. {
    63. public string DishName { get; set; }
    64. public string Size { get; set; }
    65. }
    66. // 依据.net规范, 委托的作用是处理事件时, 需要在声明时添加EventHandler后缀
    67. public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);

    上述代码中,事件的五个组成部分:customer是事件的拥有者,waiter是事件的响应者,customer.Order是事件,waiter.TakeOrder是事件处理器,"+="是事件的订阅。此外,orderEventHandler是事件用于接收订阅的委托,customer.Think是事件的触发者

    需要说明的是:

    1.依据.net规范,类的作用是传递事件信息(EventArgs)时,需在声明时添加EventArgs后缀,并实现EventArgs类(上述代码中的OrderEventArgs类)

    2.依据.net规范,委托的作用是处理事件时,需要在声明时添加EventHandler后缀(上述代码中的OrderEventHandler委托)

    事件的简略声明

    简略声明

    从形式上看事件似乎是字段,但实际上不是。事件之于委托,类似属性之于字段

    事件只能出现在+=或-=操作符左侧,但OnOrder函数的if语句中却出现在了!=操作符左侧,原因是此处为C#语法糖(简略声明下,无显式的委托字段,只能如此。Order(this, args)也是出于同样的原因)

    1. public class Customer
    2. {
    3. public event OrderEventHandler Order; // 事件
    4. public double Bill { get; set; }
    5. public void Think(string dishName, string size)
    6. {
    7. Console.WriteLine("Customer: I need {0} {1}", size, dishName);
    8. OnOrder("Cake", "Medium");
    9. }
    10. protected void OnOrder(string dishName, string size)
    11. {
    12. if (Order != null)
    13. {
    14. OrderEventArgs args = new();
    15. args.DishName = dishName;
    16. args.Size = size;
    17. Order(this, args);
    18. }
    19. }
    20. }

    完整声明

    1. public class Customer
    2. {
    3. private OrderEventHandler orderEventHandler; // 事件用于接收订阅的委托
    4. public event OrderEventHandler Order // 事件
    5. {
    6. add
    7. {
    8. orderEventHandler += value;
    9. }
    10. remove
    11. {
    12. orderEventHandler -= value;
    13. }
    14. }
    15. public double Bill { get; set; }
    16. public void Think(string dishName, string size)
    17. {
    18. Console.WriteLine("Customer: I need {0} {1}", size, dishName);
    19. OnOrder("Cake", "Medium");
    20. }
    21. protected void OnOrder(string dishName, string size)
    22. {
    23. if (orderEventHandler != null)
    24. {
    25. OrderEventArgs args = new();
    26. args.DishName = dishName;
    27. args.Size = size;
    28. orderEventHandler(this, args);
    29. }
    30. }
    31. }

    实际上大多数情况下可以直接使用C#提供的事件委托(EventHandler)来声明事件,无需自己声明事件委托

    但是需要注意EventHandler委托的参数格式是(object? sender, EventArgs e),被委托的函数中需要做里氏转换(此即为何自定义的XXXEventArgs类最好派生自EventArgs类的原因)

    1. private static void Main(string[] args)
    2. {
    3. Customer customer = new();
    4. Waiter waiter = new();
    5. customer.Order += waiter.TakeOrder;
    6. customer.Think("Cake", "Medium");
    7. }
    8. public class Customer
    9. {
    10. public event EventHandler Order;
    11. public double Bill { get; set; }
    12. public void Think(string dishName, string size)
    13. {
    14. Console.WriteLine("Customer: I need {0} {1}", size, dishName);
    15. OnOrder("Cake", "Medium");
    16. }
    17. protected void OnOrder(string dishName, string size)
    18. {
    19. if (Order != null)
    20. {
    21. OrderEventArgs args = new();
    22. args.DishName = dishName;
    23. args.Size = size;
    24. Order(this, args);
    25. }
    26. }
    27. }
    28. public class Waiter
    29. {
    30. public void TakeOrder(Object customer, EventArgs e)
    31. {
    32. // 里氏转换
    33. Customer customer_ = customer as Customer;
    34. OrderEventArgs e_ = e as OrderEventArgs;
    35. Console.WriteLine("Waiter: I will serve you the dish - {0} {1}", e_.Size, e_.DishName);
    36. double basePrice = 10;
    37. switch (e_.Size)
    38. {
    39. case "Small":
    40. basePrice *= 0.5;
    41. break;
    42. case "Large":
    43. basePrice *= 1.5;
    44. break;
    45. default:
    46. break;
    47. }
    48. customer_.Bill += basePrice;
    49. Console.WriteLine("Waiter: You need to pay ${0}", customer_.Bill);
    50. }
    51. }
    52. // 依据.net规范, 类的作用是传递事件信息(EventArgs)时, 需在声明时添加EventArgs后缀, 并实现EventArgs类
    53. public class OrderEventArgs : EventArgs
    54. {
    55. public string DishName { get; set; }
    56. public string Size { get; set; }
    57. }

    结语

    事件基于委托,但不等同于委托 

  • 相关阅读:
    机器学习【pandas文件读取与存储、高级处理、电影案例分析】
    前端研习录(16)——JavaScript引入、注释及输出方式讲解及示例说明
    网络面试-ox09 http是如何维持用户的状态?
    【Data Mining】Introduction
    Jenkins 的构建时执行时间问题
    网络I/O模型
    玩转ansys——微机械车轮的实体建模与网格化
    Web安全基础
    OpenCvSharp Tracker 目标追踪
    项目中用的网关Gateway及SpringCloud
  • 原文地址:https://blog.csdn.net/Mudrock__/article/details/136218601