C#知識體系(二)用案例來理解委託與事件

上一篇博客講到了LinQ和lambda的常用方法 還有很多我們未知但c#設計團隊已經爲我們封裝好的類和方法。隨着我們不斷的熟悉C#語言,漸漸的就會接觸到其他的知識點,委託、事件、反射、線程、同步,異步、IO、套接字。。。這些東西我們平常用到的不多,都是些概念性的東西,也許是因爲不熟悉而可以迴避了使用這些東西,不可否認的是 就算不用這些我們依然能想到問題的解決辦法。但是幾乎所有語言都會有這些概念,因爲在某些場景它們能發揮不可思議的能力。

       其實我到現在還是沒有掌握委託和事件,在工作或者設計中也儘量迴避使用,但如果想走技術這條路,這塊硬骨頭必須趁早啃了它。我們一直在疑惑:爲什麼要用委託 很想問別人 什麼場景下用到委託 反正我是沒有得到我想要的答案 所以也不能深刻的體會它的妙用。本文將講訴普通的委託,事件的委託,有參 無參 有返回值的 以上都是同步的委託,異步的到後面的異步和套接字再講。

基本語法

 //無返回值 
public delegate void delegateName() ;//無參 無返回值
 public delegate void delegateName(int a,int b) ;//2個參數 無返回值
 public delegate void delegateName(object o) ;//任意類型 無返回值
 public delegate void delegateName(object o,EventArgs args) ;//任意類型,第2個參數爲一個事件對象或者基礎了該事件對象的子類 無返回值
 //有返回值 
public delegate bool delegateName() ;//無參 有返回值
public delegate bool delegateName(object) ;//與參 有返回值 這個是不是有點熟悉呢 它就是我們常用的userlist.where()方法 要求傳參的委託 返回布爾 帶一個object參數或多個原型如下 Func<TSource, bool> predicate 最後一個參數爲返回值
 public delegate object delegateName(int a,int b) ;//2個參數 有返回值
 public delegate List<object> delegateName(object o) ;//任意類型 有返回值
 public delegate bool delegateName(object o,EventArgs args) ;//任意類型,第2個參數爲一個事件對象或者基礎了該事件對象的子類 有返回值

基本使用大家可能都略知一二,那就來點案例加深理解,來個大家熟悉的場景 QQ空間。

案例分析     

    場景如下:

         QQ空間發佈說說大家都常用,每當我們更新了說說或者日誌,好友進空間的時候總能知道是誰更新了說說。但他是怎麼知道你更新了的呢?

         分析:

           實現1:在每個用戶所屬的表裏面存有每個好友的上一次更新的說說編號,當進空間初始化時根據每個編號去和好友空間最新的編號對比,獲取大於當前編號的日誌,如果好友上百估計很慢

           實現2:用戶每次發佈說說時,遍歷他所有好友,主動插入一條說說編號到他好友說說更新表裏面或者緩存表中,當好友進空間時 根據未讀說說狀態的編號去查詢加載出來

           ...

          實現方式多種多樣,具體用的什麼方法我也猜不出來 如果有熟悉的或者來自鵝廠的大神請給我們解惑!

       

         接下來的案例代碼是以第二種方式來實現觀察者模式的(發佈者與訂閱者)

 功能分析

    在這個場景中,有當前QQ用戶發佈說說(發佈者),他所有的好友(訂閱者),還有說說(日誌)實體

這裏面會出現3個對象

   User QQ Log 

 public  class User
    {
      public User() { }

      public event PubLogHanler Handler;
      public User(int id,QQ qq)
      {
          ID = id;
          QQ = qq;
          
      }
      
        public int ID { get; set; }
       //用戶的QQ,裏面包含好友列表等信息
        public QQ QQ { get; set; }
        
       //發說說(用戶方法)
        public void PubLog(Log log) 
        {
            Console.WriteLine("發佈說說中...");
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("====================================");
            sb.AppendLine(log.Title);
            sb.AppendLine("  "+log.Content);
            sb.Append("\t"+log.Author.ToString()+"\t");
            sb.AppendLine(log.PubTime);
            sb.AppendLine("====================================");
            Console.WriteLine(sb.ToString());
           
            //觸發事件

            OnPubLog(this, new PubLogHanlerArgs(log));
            
        }
        public void OnPubLog(object o, PubLogHanlerArgs args)
        {
            if (Handler != null)
            {

                Handler(o, args);
            }
           
        }
    }

然後是QQ實體對象,它包含好友列表等信息 這裏只列舉了好友列表

   public class QQ
    {
       public QQ(){}
       public QQ(int id,List<User>list)
       {
           ID = id;
           FriendList = list;
       }
       //QQ號
        public int ID { get; set; }
       //好友列表
        public List<User> FriendList { get; set; }
    }

接下來是說說實體對象

   /// <summary>
    /// 說說類
    /// </summary>
    public class Log
    {

        public Log() { }
        public Log(string title, string content, int author, string time)
        {
            Title = title;
            Content = content;
            Author = author;
            PubTime = time;
        }
        public string Title { get; set; }
        public string Content { get; set; }
        //(QQID)作者
        public int Author { get; set; }
        public string PubTime { get; set; }

    }

調用類裏面弄點初始化數據

         User user1 = new User(1, new QQ(56521321, new List<User>() { }));
            User user2 = new User(1, new QQ(28121315, new List<User>() { }));
            User user3 = new User(1, new QQ(565221, new List<User>() { }));
            //new 一個用戶 擁有一個QQ號和3個好友
            User currUser = new User(1, new QQ(565214359, new List<User>() { user1, user2, user3 }));

如果是普通的 肯定就是直接發說說了

  //發說說
            currUser.PubLog(new Log("我的第一個說說","說說開通了可以分享咯",currUser.QQ.ID,DateTime.Now.ToString("M.d HH:mm:ss")));
   //循環遍歷所有好友
            foreach (var item in currUser.QQ.FriendList)
            {
                ////推送 存儲日誌編號到好友的待加載列表裏面
            }

 

這樣其實也算是實現功能了,如果要求不高那就提交代碼可以領盒飯了。

好吧  到現在爲止也沒委託啥事呀!!!

委託的定義和快遞的職責是一樣一樣的:發件人委託給快遞,快遞再投送給接收者。你說其實他也可以直接給接收者,但他爲什麼要委託快遞呢 懶!就是懶 哈哈 這樣理解也是奇葩了 !

 你讓我說說爲啥委託非用不可 那我還真答不上來,就像我們也可以不用快遞一樣,只不過代價就大了,可以想象那場景,網上賣件衣服都要送出國再自己回來,那酸爽。。。

既然機智的選擇用快遞,那我們就定義一個送快遞的吧

 public delegate void delegateEMS(User o, Log log);
//再定義一個快遞送快遞的方法
  public static void PubEms(User user,Log log)
        {
            foreach (var item in user.QQ.FriendList)
            {
                Console.WriteLine("已推送說說標題爲: " + log.Title + " 的說說給:" + item.QQ.ID);
                //投送 發往指定人
            }
        }

然後就是調用

            User user1 = new User(1, new QQ(56521321, new List<User>() { }));
            User user2 = new User(1, new QQ(28121315, new List<User>() { }));
            User user3 = new User(1, new QQ(565221, new List<User>() { }));
            //new 一個用戶 擁有一個QQ號和3個好友
            User currUser = new User(1, new QQ(565214359, new List<User>() { user1, user2, user3 }));
            Log temp = new Log("我的第一個說說", "說說開通了可以分享咯", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss"));
            //發說說
            currUser.PubLog(temp);
 

            delegateEMS ems = new delegateEMS(PubEms);
            ems(currUser, temp);

然後就沒有然後了 等着收貨的給差評吧!

好吧說跑題了,繼續回來說說QQ空間的事,剛纔講快遞是爲了加深大家對於委託的理解,我們用到了普通的委託有參無返回 但是發現每發一篇博客就要多寫一次  大量重複的代碼 我們接下來改爲事件+委託

委託的進階使用  事件+委託

事件可以說是無處不在 鼠標點擊事件 移動事件就不說了  之所以用到事件就是爲了實現觸發器的效果  刪除一條記錄就郵件通知相關負責人 出現多少個警告也會通知 在我們項目中比較常用

基本語法

public event PubLogHanler Handler; //這裏事件類型是自定義的
    //定義推送委託
    public  delegate void PubLogHanler(object o, PubLogHanlerArgs args);
    //發佈日誌觸發
  public  class PubLogHanlerArgs : EventArgs
    {
      public PubLogHanlerArgs(){}
      public PubLogHanlerArgs(Log log)
      {
          LogInfo = log;
      }
        public Log LogInfo { get; set; }

      
    }

這裏自定義了一個說說發佈的事件,它繼承EventArgs類,是任何事件的基類,觸發一個事件時會記錄下說說的信息

  public void PubLog(Log log) 
        {
            Console.WriteLine("發佈說說中...");
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("====================================");
            sb.AppendLine(log.Title);
            sb.AppendLine("  "+log.Content);
            sb.Append("\t"+log.Author.ToString()+"\t");
            sb.AppendLine(log.PubTime);
            sb.AppendLine("====================================");
            Console.WriteLine(sb.ToString());
           
            //觸發事件

            OnPubLog(this, new PubLogHanlerArgs(log));
            
        }
        public void OnPubLog(object o, PubLogHanlerArgs args)
        {
            if (Handler != null)
            {

                Handler(o, args);
            }
           
        }

從上面的代碼可以看到 調用發佈說說方法後會觸發事件,然後事件會去執行推送方法達到觸發器的效果

     //推送事件
        static void currUser_Handler(object o, PubLogHanlerArgs args)
        {
            User user = o as User;
            if(user!=null)
            {
             foreach (var item in user.QQ.FriendList)
            {
                Console.WriteLine("已推送說說標題爲: "+args.LogInfo.Title+" 的說說給:"+item.QQ.ID);
                 //推送 存儲日誌編號到好友的待加載列表裏面
            }
            }
           
           
            
        }

其實上面只是個方法,只是參數看起來是事件罷了,接下來就是綁定事件

     //推送給好友
            currUser.Handler += new PubLogHanler(currUser_Handler);
  //發說說
            currUser.PubLog(new Log("我的第一個說說", "說說開通了可以分享咯", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss")));


            currUser.PubLog(new Log("終於上博客園首頁啦", "博客園是個學習的好地方", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss")));

事件的妙處就在於可以綁定多個事件 ,叫多播委託。再定義一個事件

 //已通知更新緩存
        static void UpdateCache_Handler(object o, EventArgs args)
        {
            User user = o as User;
            if (user != null)
            {

                Console.WriteLine("\n<*>已通知更新緩存<*>\n");
                //已通知更新緩存
                
            }



        }

調用代碼如下

 User user1 = new User(1, new QQ(56521321, new List<User>() { }));
            User user2 = new User(1, new QQ(28121315, new List<User>() { }));
            User user3 = new User(1, new QQ(565221, new List<User>() { }));
            //new 一個用戶 擁有一個QQ號和3個好友
            User currUser = new User(1, new QQ(565214359, new List<User>() { user1, user2, user3 }));





            //推送給好友
            currUser.Handler += new PubLogHanler(currUser_Handler);
            //通知更新緩存
            currUser.Handler += new PubLogHanler(UpdateCache_Handler);
            //發說說
            currUser.PubLog(new Log("我的第一個說說", "說說開通了可以分享咯", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss")));


            currUser.PubLog(new Log("終於上博客園首頁啦", "博客園是個學習的好地方", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss")));

            Console.ReadKey();

總結

委託和事件講到這裏也接近尾聲了,我這也是臨時抱佛腳,在網上看了寫實例代碼 自己實操了一遍就根據自己的理解發上來了。很多地方估計語句不是太通順甚至概論都沒理明白,如果有錯誤之處還望大家指正,免得誤人子弟!

如果有需要源代碼的話我稍後會發上來的。下一篇博客 我會講一講反射和線程,並且把我寫的一個xml json datatable List 相互轉換 的dll和代碼分享給大家,請大家關注我的博客http://www.cnblogs.com/jingch/ 

源碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章