您的位置:首页 > 编程语言 > C#

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

2017-11-27 18:01 393 查看
有些时间 不用 c#了 ,想 写 委托 和 事件 又会 卡下 ,之前也没认真总结过。干脆 做个小结 。

什么是委托:狭义,不安全函数的指针。不安全在哪里: 任何地方都可以调用委托对象。(实际上委托对象才是[b]函数的指针,而delegate只是一个语法)[/b]

什么是事件:狭义,安全的函数指针,安全在哪里:只允许包含事件的类,的内部调用。

联系和区别:

delegate关键字类比于class关键字, 定义的是一种委托类型,需要再创建委托对象.

用event关键字来修饰创建委托对象行为,那么就成了一个特殊的委托对象,就是事件 。

所以delegate是类,event是修饰语,好比private.

光板 delegate对象,可以到处调用它。event 修饰的[b]delegate对象,只能在 “[b]event 修饰的[b]delegate对象” 所属类中使用。[/b][/b][/b]

[b][b][b]还有一点语法糖,event只允许+=,而不支持=.因为=,可能会在你需要添加的时候,写成=,会覆盖掉之间的方法。[/b][/b][/b]

写完,终于感觉好点了。

委托 :

1.概念:个人 理解 ,加强版的 函数指针,可以存放多个函数 指针 ,算是函数指针集合。但是提供了异步的高级特性(另开一个线程去调用这个函数指针)

2。 用途:需要对某个方法进行多态或延迟执行的情况下。表现在某个固定模块,先写好函数意图,实现由模块外定义和修改。而固定模块不修改代码。

但会导致同样满足要求的工厂模式乱入,区分,工厂模式是在对象级别的延迟和多态。而委托是方法级别的延迟和多态。

3.常用用法举例:处理消息模块(固定模块),模块编写处理流程。具体哪个方法处理或哪几个方法处理 ,由外部定义实现 。

4.缺点:委托无限制,可以直接在任何地方,使用委托对象,进行调用。而事件,限制在包含事件的类内部。

4.简介使用:一个类A里面定义delegate,并定义delegate对象,那么类A的方法FUNB就可以把delegate对象当作函数一样调用。

主函数 给 类A的对象中的delegate对象,附加上和delegate原型相同的函数名FUNC。那么主函数,就可以调用A的方法[b]FUNB。 而最终是调用附加上的FUNC方法。[/b]

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);//调用模块对象的方法。

// myKernel.impHandle(recvMsg);//编译 通过,对于大多数面向对象设计框架下情况下。非常危险。因为委托一般是在类内部,调用的。

Thread.Sleep(2000); } }


2.事件

概念:对委托的实例化提供了一些限制的一种语法 。public event PMSGHandle impHandle public PMSGHandle impHandle 。2个语句。都是生产委托对象。但是加了event后。这个对象进行了一些安全操作限制。

用途:跟委托一样。

   小结:外部调用事件,会有编译错误。KernelModule.impHandle”只能出现在 += 或 -= 的左边(从类型“KernelModule”中使用时除外)

  举例:

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);//调用模块对象的方法。

      myKernel.impHandle(recvMsg);//编译错误 KernelModule.impHandle”只能出现在 += 或 -= 的左边(从类型“KernelModule”中使用时除外)  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);
// }
//}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: