上一篇博客講到了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/