C#发布订阅模式详解

45 篇文章 0 订阅
15 篇文章 5 订阅

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

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

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

设计实验:

测试上述问题.
使用普通委托和匿名方法和 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($"\n{this.status} -> {status} 大家开始学习嗷");
   this.status = status;
   if (!(StudyWithMe is null))
   {
    StudyWithMe(status);
    // 触发方法中需要调用委托
   }
  }
}

// 订阅者定义
{ // public class Stu
  // 以下的内容用于添加和替换上述 Stu 类中的代码
  public void OnClass(Teacher teacher)
  {
      teacher.StudyWithMe += StudyWithTeacher;
  } // 订阅者订阅发布者方法
  public void QuitClass(Teacher teacher)
  {
      teacher.StudyWithMe -= StudyWithTeacher;
  } // 订阅者取消订阅发布者方法
  public void StudyWithTeacher(Teacher.Status status)
  {
      if (this.status == Status.摆)
      {
          Console.WriteLine($"{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;
  }
}

// 过程调用
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);
    // 订阅者将以此做出响应
}

other -> teaching 大家开始学习嗷
Emotion: 好的收到!
Jayden: 啊对对对
LongX: 好的收到!
DaMo: 好的收到!

使用 .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); // 调用事件触发方法.
  }
}

蟹老板: 大家加油哦!
海绵宝宝收到做清洁的工作, 大概需要6分钟, 蟹老板.
章鱼哥收到做清洁的工作, 大概需要5分钟, 蟹老板.
派大星收到做清洁的工作, 大概需要10分钟, 蟹老板.
海绵宝宝收到记账的工作, 大概需要12分钟, 蟹老板.
章鱼哥收到记账的工作, 大概需要10分钟, 蟹老板.
派大星收到记账的工作, 大概需要20分钟, 蟹老板.

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!");
   }
}

Try!
Finally!

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值