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

2021-04-26 03:27

阅读:338

标签:访问   情况   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);
// }
//}


}

 

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

标签:访问   情况   generic   方便   调用函数   col   关系   eve   nms   

原文地址:http://www.cnblogs.com/lsfv/p/7905366.html


评论


亲,登录后才可以留言!