• C#事件订阅发布实现原理详解


    这篇文章主要介绍了C#事件订阅发布实现原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    我们用一个简单的例子,来说明一下这种消息传递的机制。

    有一家三口,妈妈负责做饭,爸爸和孩子负责吃。。。将这三个人,想象成三个类。

    妈妈有一个方法,叫做“做饭”。有一个事件,叫做“开饭”。做完饭后,调用开发事件,发布开饭消息。

    爸爸和孩子分别有一个方法,叫做“吃饭”。

    将爸爸和孩子的“吃饭”方法,注册到妈妈的“开饭”事件。也就是,订阅妈妈的开饭消息。让妈妈做完饭开饭时,发布吃饭消息时,告诉爸爸和孩子一声。

    这种机制就是C#中的,订阅发布。下面我们用代码实现:
      class Program
    {
    public static void Main(string[] args)
    {
    //实例化对象
    Mom mom = new Mom();
    Dad dad = new Dad();
    Child child = new Child();

      //将爸爸和孩子的Eat方法注册到妈妈的Eat事件
      //订阅妈妈开饭的消息
      mom.Eat += dad.Eat;
      mom.Eat += child.Eat;
    
      //调用妈妈的Cook事件
      mom.Cook();
    
      Console.Write("Press any key to continue . . . ");
      Console.ReadKey(true);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    }

    public class Mom
    {
    //定义Eat事件,用于发布吃饭消息
    public event Action Eat;

    public void Cook()
    {
      Console.WriteLine("妈妈 : 饭好了");
      //饭好了,发布吃饭消息
      Eat?.Invoke();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    }

    public class Dad
    {
    public void Eat()
    {
    //爸爸去吃饭
    Console.WriteLine(“爸爸 : 吃饭了。”);
    }
    }

    public class Child
    {
    public void Eat()
    {
    //熊孩子LOL呢,打完再吃
    Console.WriteLine(“孩子 : 打完这局再吃。”);
    }
    }
    class Program
    {
    public static void Main(string[] args)
    {
    //实例化对象
    Mom mom = new Mom();
    Dad dad = new Dad();
    Child child = new Child();

      //将爸爸和孩子的Eat方法注册到妈妈的Eat事件
      //订阅妈妈开饭的消息
      mom.Eat += dad.Eat;
      mom.Eat += child.Eat;
    
      //调用妈妈的Cook事件
      mom.Cook();
    
      Console.Write("Press any key to continue . . . ");
      Console.ReadKey(true);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    }

    public class Mom
    {
    //定义Eat事件,用于发布吃饭消息
    public event Action Eat;

    public void Cook()
    {
      Console.WriteLine("妈妈 : 饭好了");
      //饭好了,发布吃饭消息
      Eat?.Invoke();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    }

    public class Dad
    {
    public void Eat()
    {
    //爸爸去吃饭
    Console.WriteLine(“爸爸 : 吃饭了。”);
    }
    }

    public class Child
    {
    public void Eat()
    {
    //熊孩子LOL呢,打完再吃
    Console.WriteLine(“孩子 : 打完这局再吃。”);
    }
    }
      运行结果:
    在这里插入图片描述
    爷爷奶奶来做客了怎么办呢?和爸爸孩子一样,写个Eat方法,同样注册到妈妈的开饭事件就好了。

    发布订阅模式
    委托可以通过简单的加减实现封装多个方法,但容易出现封装了多个方法的委托被赋值号一下子都给替换掉的问题,即外部对象会影响其他对象对发布者的订阅。

    委托类可以定义在发布者类之中吗? 可以的.

    实现发布订阅模式的土方法:声明委托类,在发布者类中声明委托类成员,定义发布方法(事件触发),声明订阅类,在订阅类中声明订阅方法(绑定到订阅者的委托(事件)成员中)。

    设计实验:

    测试上述问题.
    使用普通委托和匿名方法和 Lambda 表达式.
    测试匿名函数情况下的变量作用域.
    // 发布者定义
    public delegate void TeachEvent(Teacher.Status status);
    // 声明委托类, 作为发布者中用于存放订阅者处理方法的委托对象原型
    public class Teacher
    {
    public enum Status
    {
    other, teaching
    }
    public TeachEvent StudyWithMe;
    // 定义委托对象, 作为发布者公共成员
    private Status status = Status.other;
    public void ClassBegin(Status status)
    // 定义触发发布者发布事件的方法
    {
    Console.WriteLine(KaTeX parse error: Undefined control sequence: \n at position 2: "\̲n̲{this.status} -…“{name}: 啊对对对”);
    return;
    }
    Console.WriteLine($“{name}: 好的收到!”);
    } // 订阅者收到发布者消息后的处理方法
    // 并不是订阅者收到消息以后启动处理方法
    // 而是发布者调用订阅者的方法
    public enum Status
    {
    学, 摆
    }
    public Status status ;

    public Stu(string name, int age, int score, Status status)
    {
    id = _id;
    _id++;
    this.age = age;
    avgScore = score;
    this.name = name;
    this.status = status;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    C#中事件委托订阅发布模式
    rar

    0星
    超过10%的资源
    36KB

    下载
    // 过程调用
    static vois Main()
    {
    // 续前面的主方法
    // 包括 Stu[] stus 数组
    Teacher teacher = new Teacher();
    foreach (Stu stu in Stus)
    { // 批量订阅
    stu.OnClass(teacher);
    }
    stu4.QuitClass(teacher); // 其中一个取消订阅
    teacher.ClassBegin(Teacher.Status.teaching);
    // 订阅者将以此做出响应
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    other -> teaching 大家开始学习嗷
    Emotion: 好的收到!
    Jayden: 啊对对对
    LongX: 好的收到!
    DaMo: 好的收到!
    1
    2
    3
    4
    5
    使用 .net 类库EventHandler类
    // 这里使用老板的员工的情况来模拟
    // 定义发布者和订阅者的基类
    public class Person
    {
    public string name;
    }
    public class Boss : Person
    {
    // event 关键字的作用是:
    // Krabs.WorkForMe = null; // 在 Main 方法中
    // 错误 CS0070 事件“Boss.WorkForMe”只能出现在 += 或 -= 的左边(从类型“Boss”中使用时除外)
    public event EventHandler WorkForMe;
    // EventHandler 类是 .net 自带的类, 在类中声明其成员对象的实例即可
    // EventHandler 类的原型是:
    // public delegate void EventHandler(object sender, EventArgs e);
    // 注意后面定义订阅者处理方法要按照此方法原型定义.
    // object sender 指的是发布者, 一般在发布者触发方法定义时用 this
    // EventArgs e 需要额外传入的参数, 需要建一个继承于 EventArgs 的消息类.
    public void WorkNow(string encourage,Work[] works)
    { // 发布者激活 EventHandler 绑定的订阅者方法
    Console.WriteLine(this.name + “: " + encourage);
    if (WorkForMe is null)
    {
    Console.WriteLine(“坏了, 没人干活了.”);
    }
    else
    {
    foreach (Work work in works)
    {
    WorkForMe(this, work);
    // 调用委托的事件处理方法.
    // this 传入发布者本身
    // 这个循环是遍历所有触发事件的消息
    }
    }
    }
    public Work SetWork(string name, int load)
    { // 设置通过发布者设定事件消息的工厂方法, 这里不是常规的用法.
    Work work = new Work(name, load)
    {
    boss = this
    };
    return work;
    }
    public Boss(string name)
    {
    this.name = name;
    }
    }
    // 定义订阅者
    public class Worker : Person
    {
    public void WorkFor(object sender,EventArgs e)
    { // 定义 EventHandler 委托原型的方法, 传入两个参数
    // 需要记得把传入的两个参数的类型更正(强制转换).
    Boss boss = (Boss)sender;
    Work work = (Work)e;
    Console.WriteLine($”{name}收到{work.name}的工作, 大概需要{work.workLoad/workSpeed}分钟, {boss.name}.");
    }
    private int workSpeed;
    public Worker(string name, int speed)
    {
    this.name=name;
    workSpeed = speed;
    }
    }
    // 定义事件消息
    public class Work : EventArgs // 记得继承 EventArgs 类
    {
    public string name = “default”;
    public int workLoad = 1;
    public Boss boss;
    public Work(string name, int load)
    {
    this.name = name;
    this.workLoad = load;
    }
    }

    internal class Program
    {
    static void Main(string[] args)
    {
    Boss Krabs = new Boss(“蟹老板”);

      Worker SpongBob = new Worker("海绵宝宝", 5);
      Worker SquidwardTentacles = new Worker("章鱼哥", 6);
      Worker PatrickStar = new Worker("派大星", 3);
      Worker[] wokers = { SpongBob, SquidwardTentacles, PatrickStar };
      foreach (Worker w in wokers)
      {
          Krabs.WorkForMe += w.WorkFor;
          // 绑定委托
      }
    
      Work cleaning = Krabs.SetWork("做清洁", 30);
      Work accounting = Krabs.SetWork("记账", 60);
      Work[] works = { cleaning, accounting };
    
      Krabs.WorkNow("大家加油哦!", works); // 调用事件触发方法.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    蟹老板: 大家加油哦!
    海绵宝宝收到做清洁的工作, 大概需要6分钟, 蟹老板.
    章鱼哥收到做清洁的工作, 大概需要5分钟, 蟹老板.
    派大星收到做清洁的工作, 大概需要10分钟, 蟹老板.
    海绵宝宝收到记账的工作, 大概需要12分钟, 蟹老板.
    章鱼哥收到记账的工作, 大概需要10分钟, 蟹老板.
    派大星收到记账的工作, 大概需要20分钟, 蟹老板.
    1
    2
    3
    4
    5
    6
    7
    finally关键字
    public static void TryFinally()
    {
    int x = 6,y;
    y = int.Parse(Console.ReadLine());
    // 输入0
    try
    {
    Console.WriteLine(“Try!”);
    x /= y; // 除0异常
    Console.WriteLine(“Exception!”); // 被捕捉, 此行不运行
    }
    catch
    {
    return; // 虽然捕捉处理是返回, 但会先强制处理 finally 语句块
    }
    finally
    {
    Console.WriteLine(“Finally!”);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    0
    Try!
    Finally!

  • 相关阅读:
    svn(乌龟svn)和SVN-VS2022插件(visualsvn) 下载
    软件测试中如何测试算法?
    HuggingFace 国内下载 阿里云盘下载速度20MB/s
    sql-labs第46关(order by盲注脚本)
    算法自学__树的重心
    java_study
    linux常用命令
    聊聊 C++ 和 C# 中的 lambda 玩法
    C++11标准模板(STL)- 算法(std::reverse)
    【软件工程】【第一章概述】d2
  • 原文地址:https://blog.csdn.net/weixin_41883890/article/details/126189294