c# の 事件

滄海一聲笑·rush發表於2020-12-18




1.事件的概念

事件是作為消i息的通知者,書寫方便,快捷。在模組之間劃定了清晰的界限,提高了應用程式的可維護性和重用性。
用白話說,就是“有事”發生了,然後事件作為通知者把發生的事儲存起來,然後再發給多個需要響應的觀察者。
這個沒做過的人,不大好理解:
打個比方:有一群賊,有毛賊、飛賊等等,而我是放哨的,所我也就是那個所謂“事件”。作為一個非常優秀的放哨的。我預先要把“有事發生”後的情況根據賊的種類進行儲存成相應的黑話,例如對於飛賊我要儲存–“從屋頂逃跑”,對於毛賊我要儲存–“從後門溜走”之類的黑話。這時候“有事”發生了–“主人回來了”或“警察來了”,我就通知飛賊“從屋頂逃跑”,通知毛賊“從後門溜走”……
我建議樓主看一看觀察者模式,事件其實就是在底層封裝了那個觀察者模式而已。而上面例子,放哨的就是通知者,賊就是觀察者,而主人和警察就是被觀察者。事件就是根據情況進行不同的響應,發出一系列不同或相同通知(訊息)給作為“觀察者”的類。

在以往我們編寫這類程式中,往往採用等待機制,為了等待某件事情的發生,需要不斷地檢測某些判斷變數,而引入事件程式設計後,大大簡化了這種過程:





2.事件需要注意的地方:

  1. 在類裡面宣告。
  2. 釋出者是一個類,訂閱者是一個類。
  3. 可以使用+=-= 新增或者刪除委託。

釋出者和訂閱者模式:

釋出者:定義了一系列其他部分可能感興趣的事件。其他類可以 “註冊” 以獲取通知。

訂閱者: 當事件發生時,釋出者 “觸發事件”,然後執行訂閱者提交的所有事件。



3.例子

1.第一個事件例子

namespace VsCore_Demo {

    public delegate void SaySomething (string name);
    class Program {
        public void SayHello (string name) {
            Console.WriteLine ("Hello," + name + "!");
        }
        public void SayNiceToMeetYou (string name) {
            Console.WriteLine ("Nice to meet you," + name + "!");
        }

        public event SaySomething come;
        public event SaySomething come2;



        public void test () {

            //宣告兩個委託.
            //宣告委託的時候,傳進去的引數是方法名。
            SaySomething sayhello = new SaySomething (SayHello);
            SaySomething saynice = new SaySomething (SayNiceToMeetYou);

            //把兩個委託新增到事件裡
            come += sayhello;
            come += saynice;

            //事件發生了。
            //觸發事件是用事件直接加引數。
            come ("張三");
            System.Console.WriteLine("***********");
        }



        static void Main (string[] args) {
            Program program = new Program ();
            program.test ();
            Console.Read ();
        }
    }
}

事件宣告是這樣的:

 public event SaySomething come;

這樣表明了,come這個事件,只和SaySomething這個委託打交道。當事件發生的時候,由事件去呼叫這些個委託,然後由委託呼叫具體的實現方法

例項化委託,注意我們用到了new關鍵字,就好像例項化一個類一樣,然後傳入一個引數,但這個引數不是string型別、也不是int型別,而是一個方法名。

            SaySomething sayhello = new SaySomething (SayHello);
            SaySomething saynice = new SaySomething (SayNiceToMeetYou);

我們回過頭來再看一下“事件”的定義:
public event SaySomething come;

這裡已經指出了“委託”的名字,所以,我們可以直接將方法加到事件上,而省略“委託”的例項化過程,因此上面的test()方法可以簡單寫為:

public void test()
 {      
 come += SayHello;    
        come += SayNiceToMeetYou;   
        come("張三"); 
 }

激發委託的時候,又回去了抽象了,直接把引數穿進去就行,中間是啥過程,他不管用。

委託+事件是觀察者模式的一個典型例子,所謂的委託其實就是觀察者,它會關心某種事件,一旦這種事件被觸發,這個觀察者就會行動

2. 事件完成:貓叫,主人醒,老鼠跑 的經典面試題

具體程式碼如下:
有幾點需要注意:

  • CatCall?.Invoke(); 喚起事件;
  • 事件 += 可以翻譯為 。貓叫引起了老鼠跑;
using System;

namespace DelegateDemo
{
    //定義貓叫委託
    public delegate void CatCallEventHandler();
    public class Cat
    {
        //定義事件
        public event CatCallEventHandler CatCall;
        //定義喚醒事件的方法
        public void OnCatCall()
        {
            Console.WriteLine("貓叫了一聲");
            //喚醒事件。
            CatCall?.Invoke();
        }
    }
    public class Mouse
    {
        //定義老鼠跑掉方法
        public void MouseRun()
        {
            Console.WriteLine("老鼠跑了");
        }
    }
    public class People
    {
        //定義主人醒來方法
        public void WakeUp()
        {
            Console.WriteLine("主人醒了");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Cat cat = new Cat();
            Mouse m = new Mouse();
            People p = new People();
            //關聯繫結 
            cat.CatCall += new CatCallEventHandler(m.MouseRun);
            cat.CatCall += new CatCallEventHandler(p.WakeUp);
            cat.OnCatCall();

            Console.ReadKey();
        }
    }
}

3.蘋果降價的程式碼:

使用的是事件的標準形式


    class Program
    {
        static void Main(string[] args)
        {
            Apple ap1 = new Apple()
            {
                Preice = 5288
            };

            //註冊事件
            // 點了 += 以後,可以使用快捷鍵生成。
            ap1.myEvent += PriceChanged;

            //調整價格,觸發事件
            ap1.Preice = 3999;

            Console.ReadKey();
        }


        //事件響應程式
        static void PriceChanged(object sender, MyEventArgs e)
        {
            Console.WriteLine("年終大促銷,iPhone 6 只賣 " + e.NewPrice +" 元, 原價 " + e.OldPrice + " 元,快來搶!"+sender.ToString());
        }
    }


    /// <summary>
    /// 事件引數類
    /// </summary>
    public class MyEventArgs : EventArgs
    {
        public MyEventArgs(decimal newPrice, decimal oldPrice)
        {
            this.NewPrice = newPrice;
            this.OldPrice = oldPrice;
        }


        public readonly decimal NewPrice;
        public readonly decimal OldPrice;
    }


    /// <summary>
    /// 事件釋出者類
    /// </summary>
    public class Apple
    {
        private decimal price;


        public decimal Preice
        {
            get
            {
                return price;
            }
            set
            {
                //如果價格變了,觸發事件通知使用者價格變了
                if (price == value)
                {
                    return;
                }


                decimal oldPrice = price;
                price = value;

                //呼叫觸發事件的方法。
                //一般啟用方法是 On + event
                OnRaiseEvet(new MyEventArgs(price, oldPrice));
            }
        }


        public event EventHandler<MyEventArgs> myEvent;

        //激發方法。
        public virtual void OnRaiseEvet(MyEventArgs e)
        {

            //如果呼叫列表不為空,則觸發
            if (myEvent != null)
            {
                myEvent(this, e);

                //event關鍵字定義的事件只能由事件源物件自己激發,外界無法通過訪問委託變數直接激發事件。
                //下面的程式碼無法編譯通過:Publisher p = new Publisher(); p.MyEvent(10);
            }
        }
    }

關於事件

  • 事件的主要目的是防止訂閱者之間相互干擾。
  • 宣告事件,直接在委託前邊加 event
  • 內外有別: 事件在類裡面的時候,可以當委託一樣用,而在類外邊,只能進行+= 和 – = 的操作
  • 一般情況下,啟用事件的方法,都預設用 On+事件 命名
  • 啟用使用 CatCall?.Invoke()
  • 使用 event 來作為關鍵字,有什麼強制規定?(1)只能在包容類,才能傳送向訂閱者發出的委託,(2)類外邊不允許使用 =。
  • sender 是用來輸出釋出者類的。
  • 對於事件來說,外部只能註冊自己+=,登出自己-=,外界不可以登出其他的註冊者,


參考:

  1. c#經典面試題—貓叫,主人醒,老鼠跑(事件的處理)
  2. https://blog.csdn.net/shangrila_ftd/article/details/70148922
  3. 蘋果降價引發瘋搶的原始碼
  4. https://zhidao.baidu.com/question/202695971.html

相關文章