标签:访问 情况 generic 方便 调用函数 col 关系 eve nms
有些时间 不用 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); // } //} }
标签:访问 情况 generic 方便 调用函数 col 关系 eve nms
原文地址:http://www.cnblogs.com/lsfv/p/7905366.html