c# 工廠模式 ,委托 ,事件。
阿新 • • 發佈:2017-11-27
委托 callback ica 2個 handle str 函數名 函數指針 urn
有些時間 不用 c#了 ,想 寫 委托 和 事件 又會 卡下 ,之前也沒認真總結過。幹脆 做個小結 。
委托 :
1.概念:個人 理解 ,加強版的 函數指針,可以存放多個函數 指針 ,算是函數指針集合。但是提供了異步的高級特性(另開一個線程去調用這個函數指針)
2。 用途:需要對某個方法進行多態或延遲執行的情況下。表現在某個固定模塊,先寫好函數意圖,實現由模塊外定義和修改。而固定模塊不修改代碼。
但會導致同樣滿足要求的工廠模式亂入,區分,工廠模式是在對象級別的延遲和多態。而委托是方法級別的延遲和多態。
3.常用用法舉例:處理消息模塊(固定模塊),模塊編寫處理流程。具體哪個方法處理或哪幾個方法處理 ,由外部定義實現 。
4.簡介使用:一個類A裏面定義delegate,並定義delegate對象,那麽類A的方法FUNB就可以把delegate對象當作函數一樣調用。
主函數 給 類A的對象中的delegate對象,附加上和delegate原型相同的函數名FUNC。那麽主函數,就可以調用A的方法FUNB。 而最終是調用附加上的FUNC方法。
class KernelModule//固定模塊,內部成員必須要有委托(函數指針),來表達意圖,而實現來自於外部。 { public delegate string PMSGHandle(string msg);//需要一個函數指針public PMSGHandle impHandle;//函數指針對象 public void ProcessMsg(string msg) {
//一些流程
if (impHandle != null)
{
//impHandle(msg);//調用函數指針來調用模塊外方法。 impHandle.BeginInvoke(msg, callbacka, "info");//特性,提供異步調用,並提供回調函數。我去。c#真是逆了天了。使用起來真方便。
}} public void callbacka(System.IAsyncResult iar) { //獲取綁定函數的引用 AsyncResult ar = (AsyncResult)iar; PMSGHandle delBp = (PMSGHandle)ar.AsyncDelegate; //等待函數執行完畢 string result = delBp.EndInvoke(iar); Console.Write("callback ret:"+ result); } } class Program { static string pmsg(string msg)//意圖的實現1 { Console.Write("process fun1:" + msg + Environment.NewLine); return "pmsg do it"; } static string pmsg2(string msg)//意圖的實現2 { Console.Write("process fun2:" + msg + Environment.NewLine); return "pmsg2 do it"; } static void Main(string[] args) { string recvMsg = "0101"; KernelModule myKernel = new KernelModule();//創建模塊對象。 myKernel.impHandle = pmsg2;//給模塊的委托賦值(把函數指針傳遞)。 //myKernel.impHandle += pmsg;//委托的特性,可以多路委托。(異步調用只能由一個委托對象) myKernel.ProcessMsg(recvMsg);//調用模塊對象的方法。 Thread.Sleep(2000); } }
2.事件
概念:對委托進行了封裝,
用途:跟委托一樣。
小結:為什麽感覺不到和委托的差異?
舉例:
class KernelModule//固定模塊,內部成員必須要有委托(函數指針),來表達意圖,而實現來自於外部。 { public delegate string PMSGHandle(string msg);//需要一個函數指針 public event PMSGHandle impHandle;//多寫一個event關鍵字。生成一個event對象,而不是一個delegate對象。 //public PMSGHandle impHandle;//函數指針對象 public void ProcessMsg(string msg) {
if (impHandle != null)
{ impHandle(msg);//調用函數指針來調用模塊外方法。 //impHandle.BeginInvoke(msg, callbacka, "info");//特性,提供異步調用,並提供回調函數。我去。c#真是逆了天了。使用起來真方便。
}
} public void callbacka(System.IAsyncResult iar) { //獲取綁定函數的引用 AsyncResult ar = (AsyncResult)iar; PMSGHandle delBp = (PMSGHandle)ar.AsyncDelegate; //等待函數執行完畢 string result = delBp.EndInvoke(iar); Console.Write("callback ret:"+ result); } } class Program { static string pmsg(string msg)//意圖的實現1 { Console.Write("process fun1:" + msg + Environment.NewLine); return "pmsg do it"; } static string pmsg2(string msg)//意圖的實現2 { Console.Write("process fun2:" + msg + Environment.NewLine); return "pmsg2 do it"; } static void Main(string[] args) { string recvMsg = "0101"; KernelModule myKernel = new KernelModule();//創建模塊對象。 myKernel.impHandle += pmsg2;//給模塊的事件handle添加event,不能再使用=號了(把函數指針傳遞)。 myKernel.impHandle += pmsg;//委托的特性,可以多路委托。(異步調用只能由一個委托對象) myKernel.ProcessMsg(recvMsg);//調用模塊對象的方法。 Thread.Sleep(2000); } }
思路草稿:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { ////處理消息例子v1. //public class ProcessMsg //{ // public void DoMsg(string recvMsg) // { // if (recvMsg == "0101") // { // Console.Write("login"+Environment.NewLine); // } // else if (recvMsg == "0102") // { // Console.Write("exit" + Environment.NewLine); // } // } //} //class Program //{ // static void Main(string[] args) // { // string recvMsg = "0101"; // ProcessMsg processMsg=new ProcessMsg(); // processMsg.DoMsg(recvMsg); // } //} ////處理消息例子v2. ////需求:可更換處理消息邏輯。 ////註意:假設Main函數裏面是一個固定模塊(一般來說處理消息模塊,肯定是固定不變的)。所以我們不可能去修改ProcessMsg2 processMsg = new ProcessMsg2(); ////所以通常有多種選擇:更高級的語言,如c#,最容易想到的是使用工廠模式,新建立一個管理消息類的工廠類。 ////缺點代碼稍微復雜。需要基類,繼承,工廠類。仔細思考,我們需要的並不是對象的多態,僅僅是一個方法的多態,可以把多態從對象級別縮小到方法級別。 ////使用繼承實現多態。本質上還是調用同一個函數名,根據對象不同,指向不同的函數指針。 ////所以可以有更簡單的方法,直接傳遞函數指針。 ////總結,對象多態用繼承,工廠,僅僅是某個方法的多態,使用函數指針或delegate. //public class FactoryProcessMsg //{ // public IProcessMsg GetProcessMsgFun(int ftype) // { // if (ftype == 1) // { // return new ProcessMsg(); // } // else // { // return new ProcessMsg2(); // } // } //} //public interface IProcessMsg //{ // void DoMsg(string recvMsg); //} //public class ProcessMsg:IProcessMsg //{ // public void DoMsg(string recvMsg) // { // if (recvMsg == "0101") // { // Console.Write("login" + Environment.NewLine); // } // else if (recvMsg == "0102") // { // Console.Write("exit" + Environment.NewLine); // } // } //} //public class ProcessMsg2: IProcessMsg //{ // public void DoMsg(string recvMsg) // { // if (recvMsg == "0101") // { // Console.Write("new login" + Environment.NewLine); // } // else if (recvMsg == "0102") // { // Console.Write("new exit" + Environment.NewLine); // } // } //} ////核心模塊應該是不變的,很可能是編譯為一個dll,類庫提供外部使用。 //class KernelModel_domsg //{ // public void processmsg(string msg,int ftype) // { // FactoryProcessMsg fpm = new FactoryProcessMsg(); // iprocessMsg = fpm.GetProcessMsgFun(ftype); // iprocessMsg.DoMsg(msg); // } // private IProcessMsg iprocessMsg; //} //class Program //{ // static void Main(string[] args) // { // string recvMsg = "0101"; // KernelModel_domsg processMsg = new KernelModel_domsg(); // //通過工廠模式的創建模式,也可以說是策略模式的行為模式,實現了需求 // //修改這裏的代碼,就可以實現我們更換處理邏輯要求,而不需要更改KernelModel_domsg。 // processMsg.processmsg(recvMsg, 2); // } //} ////處理消息例子v3. ////需求:可更換處理消息邏輯。 ////註意:假設Main函數裏面是一個固定模塊(一般來說處理消息模塊,肯定是固定不變的); ////例子v2使用繼承實現多態。解決了問題。但是稍微復雜。 ////更簡單的方法,直接傳遞函數指針。c#裏面用delegate 這個類,就是更高級的函數指針。 ////可以看到使用delegate(函數指針),代碼量非常少,直達中心思想。沒有繼承,和工廠。 ////工廠適合模塊間的整體關系。而如果是簡單的單個方法的多態,使用delegate,更簡潔。 ////delegate還更適合 觀察者+訂閱者 場景。 ////新需求:不但要處理消息邏輯,還需要把消息記錄到數據庫,記錄到日誌文本,等等需求。 ////我們的快速反應是,調用數據庫,文本模塊的方法。大概是這樣吧。 ////1.消息邏輯處理 ProcessMsg myfun = new ProcessMsg();DoMsgHandle msg1 = myfun.DoMsg2;processMsg.processmsg(recvMsg, msg1); ////2.消息數據庫處理 DBModule dbModule = new DBModule();DoMsgHandle dbmsg = dbModule.DoMsg;processMsg.processmsg(recvMsg, dbmsg); ////3.消息日誌記錄 IOModule ioModule = new IOModule();DoMsgHandle iomsg = ioModule.DoMsg;processMsg.processmsg(recvMsg, iomsg); ////這樣並無不妥,稍微改進一下。可能把這3個方法,集合起來放到一個方法中。使用的時候,就不用寫3行了。 ////剛好delegate,其實已經有這個功能。 //delegate void DoMsgHandle(string recvMsg); //public class ProcessMsg //{ // public void DoMsg(string recvMsg) // { // if (recvMsg == "0101") // { // Console.Write("login" + Environment.NewLine); // } // else if (recvMsg == "0102") // { // Console.Write("exit" + Environment.NewLine); // } // } // public void DoMsg2(string recvMsg) // { // if (recvMsg == "0101") // { // Console.Write("new login" + Environment.NewLine); // } // else if (recvMsg == "0102") // { // Console.Write("new exit" + Environment.NewLine); // } // } //} ////核心模塊應該是不變的,很可能是編譯為一個dll,類庫提供外部使用。 //class KernelModel_domsg //{ // public void processmsg(string msg, DoMsgHandle msgHandle) // { // msgHandle(msg); // } //} //class Program //{ // static void Main(string[] args) // { // string recvMsg = "0101"; // KernelModel_domsg processMsg = new KernelModel_domsg(); // ProcessMsg myfun = new ProcessMsg(); // DoMsgHandle msgfun = myfun.DoMsg2; // processMsg.processmsg(recvMsg, msgfun); // } //} //////處理消息例子v4. //////需求:可更換處理消息邏輯。 //////新需求:不但要處理消息邏輯,還需要把消息記錄到數據庫,記錄到日誌文本,等等需求。 //////我們的快速反應是,調用數據庫,文本模塊的方法。大概是這樣吧。 //////1.消息邏輯處理 ProcessMsg myfun = new ProcessMsg();DoMsgHandle msg1 = myfun.DoMsg2;processMsg.processmsg(recvMsg, msg1); //////2.消息數據庫處理 DBModule dbModule = new DBModule();DoMsgHandle dbmsg = dbModule.DoMsg;processMsg.processmsg(recvMsg, dbmsg); //////3.消息日誌記錄 IOModule ioModule = new IOModule();DoMsgHandle iomsg = ioModule.DoMsg;processMsg.processmsg(recvMsg, iomsg); //////這樣並無不妥,稍微改進一下。可能把這3個方法,集合起來放到一個方法中。使用的時候,就不用寫3行了。 //////剛好delegate,其實已經有這個功能。 //delegate void DoMsgHandle(string recvMsg); ////數據庫模塊 //public class DBModule //{ // public void WriteToDb(string recvmsg) // { // string dbname = "messagedb"; // Console.Write(recvmsg + ". 已經寫入" + dbname + "到數據庫" + Environment.NewLine); // } // public void WriteToDb2(string recvmsg) // { // string dbname = "messagedb"; // Console.Write(recvmsg + ". 已經寫入" + dbname + "到數據庫,並更新了對應的新表數據" + Environment.NewLine); // } //} ////消息處理模塊 //public class ProcessMsg //{ // public void DoMsg(string recvMsg) // { // if (recvMsg == "0101") // { // Console.Write("login" + Environment.NewLine); // } // else if (recvMsg == "0102") // { // Console.Write("exit" + Environment.NewLine); // } // } // public void DoMsg2(string recvMsg) // { // if (recvMsg == "0101") // { // Console.Write("new login" + Environment.NewLine); // } // else if (recvMsg == "0102") // { // Console.Write("new exit" + Environment.NewLine); // } // } //} ////核心模塊應該是不變的,很可能是編譯為一個dll,類庫提供外部使用。 //class KernelModel_domsg //{ // public void processmsg(string msg, DoMsgHandle msgHandle) // { // //對消息進行檢測. // msgHandle(msg); // } //} ////存在問題 ////訪問度太高。可以不經過 processMsg.processmsg(recvMsg, msgfuns); 而直接調用 msgfuns(recvMsg); ////原因在於內核(KernelModel_domsg)使用的對象,在外部層,可以隨意調用。 ////修改一下。 //class Program //{ // static void Main(string[] args) // { // string recvMsg = "0101"; // KernelModel_domsg processMsg = new KernelModel_domsg(); // ProcessMsg myfun = new ProcessMsg(); // DoMsgHandle msgfuns = myfun.DoMsg2; // DBModule dom = new DBModule(); // msgfuns += dom.WriteToDb2; // processMsg.processmsg(recvMsg, msgfuns); // } //} ////處理消息例子v4. ////需求:可更換處理消息邏輯。 ////新需求:不但要處理消息邏輯,還需要把消息記錄到數據庫,記錄到日誌文本,等等需求。 ////我們的快速反應是,調用數據庫,文本模塊的方法。大概是這樣吧。 ////1.消息邏輯處理 ProcessMsg myfun = new ProcessMsg();DoMsgHandle msg1 = myfun.DoMsg2;processMsg.processmsg(recvMsg, msg1); ////2.消息數據庫處理 DBModule dbModule = new DBModule();DoMsgHandle dbmsg = dbModule.DoMsg;processMsg.processmsg(recvMsg, dbmsg); ////3.消息日誌記錄 IOModule ioModule = new IOModule();DoMsgHandle iomsg = ioModule.DoMsg;processMsg.processmsg(recvMsg, iomsg); ////這樣並無不妥,稍微改進一下。可能把這3個方法,集合起來放到一個方法中。使用的時候,就不用寫3行了。 ////剛好delegate,其實已經有這個功能。 //數據庫模塊 public class DBModule { public void WriteToDb(string recvmsg) { string dbname = "messagedb"; Console.Write(recvmsg + ". 已經寫入" + dbname + "到數據庫" + Environment.NewLine); } public void WriteToDb2(string recvmsg) { string dbname = "messagedb"; Console.Write(recvmsg + ". 已經寫入" + dbname + "到數據庫,並更新了對應的新表數據" + Environment.NewLine); } } //消息處理模塊 public class ProcessMsg { public void DoMsg(string recvMsg) { if (recvMsg == "0101") { Console.Write("login" + Environment.NewLine); } else if (recvMsg == "0102") { Console.Write("exit" + Environment.NewLine); } } public void DoMsg2(string recvMsg) { if (recvMsg == "0101") { Console.Write("new login" + Environment.NewLine); } else if (recvMsg == "0102") { Console.Write("new exit" + Environment.NewLine); } } } class MsgHandleClass { public delegate void DoMsgHandle(string recvMsg); public void FirstSet(DoMsgHandle handle) { msgHandle = handle; } public void AddFun(DoMsgHandle handle) { msgHandle += handle; } public void callit(string msg) { msgHandle(msg); } private DoMsgHandle msgHandle; } //核心模塊應該是不變的,很可能是編譯為一個dll,類庫提供外部使用。 class KernelModel_domsg { public KernelModel_domsg() { handle = new MsgHandleClass(); } public MsgHandleClass handle; public void processmsg(string msg) { //對消息進行檢測. handle.callit(msg); } } //這樣從頭到尾,除了定義了delegate void DoMsgHandle(string recvMsg); //我們之後都沒有使用 DoMsgHandle 這個變量。而是用類包含起來了。 //好處是避免直接調用DoMsgHandle(msg)而產生的失誤。 //滿足了某個委托應該存在某個固定模塊內部(不能輕易調用),而委托又必須是public。這2個矛盾體。 //只要你堅持用MsgHandleClass,來操作委托對象,就可避免這個矛盾。 //但是這個MsgHandleClass,太死板了,只能對DoMsgHandle使用。 //所以ms,自己做了個event. class Program { static void Main(string[] args) { string recvMsg = "0101"; KernelModel_domsg processMsg = new KernelModel_domsg(); ProcessMsg myfun = new ProcessMsg(); DBModule dom = new DBModule(); processMsg.handle.FirstSet(myfun.DoMsg); processMsg.handle.AddFun(dom.WriteToDb2); processMsg.processmsg(recvMsg); } } //////處理消息例子v4. //////需求:可更換處理消息邏輯。 //////新需求:不但要處理消息邏輯,還需要把消息記錄到數據庫,記錄到日誌文本,等等需求。 //////我們的快速反應是,調用數據庫,文本模塊的方法。大概是這樣吧。 //////1.消息邏輯處理 ProcessMsg myfun = new ProcessMsg();DoMsgHandle msg1 = myfun.DoMsg2;processMsg.processmsg(recvMsg, msg1); //////2.消息數據庫處理 DBModule dbModule = new DBModule();DoMsgHandle dbmsg = dbModule.DoMsg;processMsg.processmsg(recvMsg, dbmsg); //////3.消息日誌記錄 IOModule ioModule = new IOModule();DoMsgHandle iomsg = ioModule.DoMsg;processMsg.processmsg(recvMsg, iomsg); //////這樣並無不妥,稍微改進一下。可能把這3個方法,集合起來放到一個方法中。使用的時候,就不用寫3行了。 //////剛好delegate,其實已經有這個功能。 ////數據庫模塊 //public class DBModule //{ // public void WriteToDb(string recvmsg) // { // string dbname = "messagedb"; // Console.Write(recvmsg + ". 已經寫入" + dbname + "到數據庫" + Environment.NewLine); // } // public void WriteToDb2(string recvmsg) // { // string dbname = "messagedb"; // Console.Write(recvmsg + ". 已經寫入" + dbname + "到數據庫,並更新了對應的新表數據" + Environment.NewLine); // } //} ////消息處理模塊 //public class ProcessMsg //{ // public void DoMsg(string recvMsg) // { // if (recvMsg == "0101") // { // Console.Write("login" + Environment.NewLine); // } // else if (recvMsg == "0102") // { // Console.Write("exit" + Environment.NewLine); // } // } // public void DoMsg2(string recvMsg) // { // if (recvMsg == "0101") // { // Console.Write("new login" + Environment.NewLine); // } // else if (recvMsg == "0102") // { // Console.Write("new exit" + Environment.NewLine); // } // } //} ////核心模塊是不變的,很可能是編譯為一個dll,類庫提供外部使用。 //class KernelModel_domsg //{ // public delegate void DoMsgHandle(string recvMsg); // public event DoMsgHandle OnMsgEvent = null; // public void processmsg(string msg) // { // //對消息進行檢測. // if(OnMsgEvent!=null) // { // OnMsgEvent(msg); // } // } //} ////這樣從頭到尾,除了定義了delegate void DoMsgHandle(string recvMsg); ////我們之後都沒有使用 DoMsgHandle 這個變量。而是用event ////好處是避免直接調用DoMsgHandle(msg)而產生的失誤。 ////滿足了某個委托應該存在某個固定模塊內部(不能輕易調用),而委托又必須是public。這2個矛盾體。 ////只要你堅持用event,來操作委托對象,就可避免這個矛盾。 //class Program //{ // static void Main(string[] args) // { // string recvMsg = "0101"; // KernelModel_domsg processMsg = new KernelModel_domsg(); // ProcessMsg myfun = new ProcessMsg(); // DBModule dom = new DBModule(); // processMsg.OnMsgEvent += myfun.DoMsg; // processMsg.OnMsgEvent += dom.WriteToDb2; // processMsg.processmsg(recvMsg); // } //} }
c# 工廠模式 ,委托 ,事件。