C# 使用委派實作事件

// 宣告委派
public delegate void DelNotify(Publisher publisher, News news);

#region 報社
public class Publisher {
  public Publisher() { }
  public Publisher(string pName) {
    Name = pName;
  }

  public string Name;

  // 建立委派
  //public DelNotify delNotifyBreakingNews;
  // 避免被外部亂呼叫加上 event 關鍵字
  public event DelNotify delNotifyBreakingNews;

  // 可直接用定義好的委派 EventHandler
  public event EventHandler eveNotifyBreakingNews;
  public event EventHandler eveNotifyStockNews;

  #region 發佈最新消息
  public void PublishBreakingNews(string pNews) {
    News news = new News() {
      title = "最新消息",
      content = pNews
    };

    // 呼叫委派執行工作
    //delNotifyBreakingNews.Invoke(pNews);
    // 避免上一行沒訂閱者時 Invoke 會報錯,多包一個檢查的方法呼叫
    OnGetNews(this, news, delNotifyBreakingNews);
    OnGetNews(this, news, eveNotifyBreakingNews);
  }
  #endregion

  #region 發佈股市消息
  public void PublishStockNews(string pNews) {
    News news = new News() {
      title = "股市消息",
      content = pNews
    };

    OnGetNews(this, news, eveNotifyStockNews);
  }
  #endregion

  #region 發佈消息時檢查委派並執行
  /// <summary> 發佈消息時檢查委派並執行 </summary>
  protected void OnGetNews(Publisher pPublisher, News pNews, DelNotify del) {
    // 檢查有訂閱者才 Invoke
    //if (delNotifyBreakingNews != null) {
    //  delNotifyBreakingNews.Invoke(pNews);
    //}
    del?.Invoke(pPublisher, pNews);
  }

  /// <summary> 發佈消息時檢查委派並執行 </summary>
  protected void OnGetNews(Publisher pPublisher, News pNews, EventHandler del) {
    del?.Invoke(pPublisher, pNews);
  }
  #endregion
}
#endregion

#region 新聞
public class News : EventArgs {
  public string title;
  public string content;
}
#endregion

#region 訂閱者
class Subscriber {
  public Subscriber() { }
  public Subscriber(string pName) {
    Name = pName;
  }

  public string Name;

  public void NotifyMe(Publisher pPublisher, News pNews) {
    Console.WriteLine($"我是 {Name},我已經收到來自{pPublisher.Name}的{pNews.title}:{pNews.content}");
  }

  //用定義好的 EventHandler 要自己轉型
  public void NotifyMe(object sender, EventArgs eventArgs) {
    Publisher publisher = sender as Publisher;
    News news = eventArgs as News;
    Console.WriteLine($"我是 {Name},我已經收到來自{publisher.Name}的{news.title}:{news.content}");
  }
}
#endregion

class Program {
  static void Main(string[] args) {
    // 產生報社
    Publisher AppleNews = new Publisher("蘋果日報");
    Publisher BananaNews = new Publisher("香蕉日報");

    // 產生訂閱者
    Subscriber Tom = new Subscriber("Tom");
    Subscriber John = new Subscriber("John");
    Subscriber Mary = new Subscriber("Mary");
    Subscriber Lisa = new Subscriber("Lisa");

    // 訂閱即時新聞(報社的委派指定訂閱者的 NotifyMe 方法)
    AppleNews.delNotifyBreakingNews += Tom.NotifyMe;
    AppleNews.delNotifyBreakingNews += John.NotifyMe;

    AppleNews.eveNotifyBreakingNews += Tom.NotifyMe;
    AppleNews.eveNotifyBreakingNews += John.NotifyMe;
    AppleNews.eveNotifyStockNews += Mary.NotifyMe;
    AppleNews.eveNotifyStockNews += Lisa.NotifyMe;

    BananaNews.eveNotifyBreakingNews += Mary.NotifyMe;
    BananaNews.eveNotifyBreakingNews += Lisa.NotifyMe;
    BananaNews.eveNotifyStockNews += Tom.NotifyMe;
    BananaNews.eveNotifyStockNews += John.NotifyMe;

    // 委派要能讓客戶訂閱,就要能被外部使用,但委派可能被外部執行而產生意料之外的事。
    // 比如外部 Invoke 傳入假消息,訂閱者都會收到通知。
    // 解決方法是在定義委派型別前加上 event,C# 編譯器就會禁止外部來執行這個委派。
    //AppleNews.delNotifyBreakingNews.Invoke(AppleNews, new News() { title = "假消息", content = "牽手會懷孕" });

    //發送新聞時就會呼叫委派執行發送訂閱的事件      
    AppleNews.PublishBreakingNews("發生六級地震!!");
    AppleNews.PublishStockNews("美股大漲!!");
    BananaNews.PublishBreakingNews("發現 COVID-19 變種!!");
    BananaNews.PublishStockNews("台股開高走低!!");
  }
}

參考來源

https://ithelp.ithome.com.tw/articles/10228852

https://ithelp.ithome.com.tw/articles/10228906

發佈留言