1. 程式人生 > >c# 工廠模式 ,委托 ,事件。

c# 工廠模式 ,委托 ,事件。

委托 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# 工廠模式 ,委托 ,事件。