C# for Unity编程语言快速入门教程(连载15)_C#委托与事件
C#的委托(delegate)与事件(event) 其实不是一个容易理解的技术,而且很多C#书籍作者还经常把它与“观察者设计模式”(Observer模式)放在一起进行讨论(注:因为委托与事件是“Observer”设计模式的一种很好的实现方式),其实这就进一步增加了对于C#初学者的学习难度。
所以笔者打破常规,先从讲故事的方式,先来介绍"委托”。
案例故事:“老板来啦!”
2007年是中国股市非常火热的一年,某公司办公室每个员工都在热心“炒股”,为了防止老板看见,所以大家让“办公室前台”小张来注意老板;每当老板进入办公室的时候,则小张就给每个好友QQ通知,“老板来啦!”
以上案例,编写如下代码:
class Program
{
public void InformMrZhangSan(string name)
{
Console.WriteLine("{0},老板来了,张三,请关闭电脑", name);
}
public void InformMrLisi(string name)
{
Console.WriteLine("{0},老板来了,李四,请关闭电脑", name);
}
public void InformMrWangWu(string name)
{
Console.WriteLine("{0},老板来了,王五,请关闭电脑", name);
}
/// <summary>
/// (前台)小张发信息
/// </summary>
public void MrZhangSendInfo()
{
InformMrZhangSan("我是前台小张");
InformMrLisi("我是前台小张");
InformMrWangWu("我是前台小张");
}
static void Main1(string[] args)
{
Program obj = new Program();
obj.MrZhangSendInfo();
}
}
以上代码,就是一个典型的同步代码实现,也就是调用一个类型的方法,会即刻出现该方法执行的结果。相信大家很好理解以上代码。
问题来了,此时如果公司有很多员工,都要求“前台小张”来发QQ(单独)通知给他。当然还要想到,可能公司还会有新的员工加入“炒股”的队伍,也要求“前台小张”来进行“提醒”服务,这如何处理呢?
我们先考虑案例故事中的解决方案: 大家一定能够想到,其实很简单,可以建立一个“QQ提醒群”,需要提醒的公司员工,可以提前加入到这个群中。当老板进入办公室的时候,前台小张只需要在Q群里发布一条消息:“老板来了,注意啦!”即可,这样无论公司员工不断加入,还有有人离开这个群,对于小张来说是不受任何影响的。
我们按照以上的思路,重写以上类,代码如下所示:
//定义委托: 通知信息
public delegate void InformInfoHandler(string name);
//定义类
class UseDeledate
{
public void InformMrZhangSan(string name)
{
Console.WriteLine("{0},老板来了,张三,请关闭电脑", name);
}
public void InformMrLisi(string name)
{
Console.WriteLine("{0},老板来了,李四,请关闭电脑", name);
}
public void InformMrWangWu(string name)
{
Console.WriteLine("{0},老板来了,王五,请关闭电脑", name);
}
//新来的同事
public void InformMrNewComer(string name)
{
Console.WriteLine("{0},老板来了,新来的同事,请关闭电脑", name);
}
/// <summary>
/// 通知所有人
/// </summary>
/// <param name="name"></param>
/// <param name="informInfoDelegate"></param>
public void InformAll(string name,InformInfoHandler informInfoDelegate)
{
informInfoDelegate(name);
}
/// <summary>
/// (前台)小张发信息
/// </summary>
public void MrZhangSendInfo()
{
//定义委托(很类似“定义类”)
InformInfoHandler myInform = new InformInfoHandler(InformMrZhangSan);
myInform += new InformInfoHandler(InformMrLisi);
myInform += new InformInfoHandler(InformMrWangWu);
myInform += new InformInfoHandler(InformMrNewComer);
InformAll("我的前台小张",myInform);
}
static void Main(string[] args)
{
UseDeledate obj = new UseDeledate();
obj.MrZhangSendInfo();
}
}
在以上示例代码中,我们先定义了一个委托:public delegate void InformInfoHandler(string name); 这个委托的类型,必须与需要注册的方法“签名”(方法的参数列表与返回类型)一致。然后我们定义类:“UseDeledate” ,在这个类中,方法MrZhangSendInfo()就是负责方法的“注册”,这里就相当于,在小张通知Q群前,需要让需要的公司员工提前“入群”。
方法 InformAll() 的方法列表中,我们引入了委托类型: InformInfoHandler ,通过这个委托,来间接调用注册的所有方法(一共4个)。这样无论调用的方法是一个还是很多,调用方法只是用 “informInfoDelegate(name); ” 这个委托即可完成任务,实现了“调用方”与(实现功能)“方法体”本身的解耦和。
以上代码的写法主要是考虑与故事的配合,在这给出更加一般的委托定义编写方式示例:
//定义委托(通知信息)
public delegate void InformInfoHandler(string name);
class UseDeletage2
{
//声明委托类型
InformInfoHandler myInform;
public UseDeletage2()
{
//委托注册()
myInform = new InformInfoHandler(InformMrZhangSan);
myInform += new InformInfoHandler(InformMrLisi);
myInform += new InformInfoHandler(InformMrWangWu);
myInform += new InformInfoHandler(InformMrNewComer);
////委托取消注册
//myInform -= new InformInfoHandler(InformMrWangWu);
//直接用“=”赋值符号,则仅最后注册的方法有效。
//myInform = new InformInfoHandler(InformMrZhangSan);
//myInform = new InformInfoHandler(InformMrLisi);
//myInform = new InformInfoHandler(InformMrWangWu);
//myInform = new InformInfoHandler(InformMrNewComer);
}
public void InformMrZhangSan(string name)
{
Console.WriteLine("{0},老板来了,张三,请关闭电脑", name);
}
public void InformMrLisi(string name)
{
Console.WriteLine("{0},老板来了,李四,请关闭电脑", name);
}
public void InformMrWangWu(string name)
{
Console.WriteLine("{0},老板来了,王五,请关闭电脑", name);
}
//新来的同事
public void InformMrNewComer(string name)
{
Console.WriteLine("{0},老板来了,新来的同事,请关闭电脑", name);
}
/// <summary>
/// 通知所有人(此步骤可以省略)
/// </summary>
/// <param name="name"></param>
/// <param name="informInfoDelegate"></param>
//public void InformAll(string name, InformInfoHandler informInfoDelegate)
//{
// informInfoDelegate(name);
//}
/// <summary>
/// (前台)小张发信息
/// </summary>
public void MrZhangSendInfo()
{
//InformAll("我的前台小张", myInform); //省略
myInform("我是前台的小张");
}
static void Main1(string[] args)
{
Console.WriteLine("UseDeletage2 测试");
UseDeletage2 obj = new UseDeletage2();
obj.MrZhangSendInfo();
}
}
委托定义方式可以总结如下四个步骤。
A) 定义委托
B) 声明委托实例。
C) 委托注册(注册方法)
D) 调用委托
现在总结委托的一些普遍规则:
规则1: 委托本质上就是一个“类”,能定义类的地方,都可以定义委托。
(可以通过反编译软件,进行查看证明,委托就是继承了 [mscorlib]System.MulticastDelegate 的特殊类)
规则2: 使用委托技术,可以实现在“在方法的参数中传方法”。
即: 把一个方法作为参数,在另一个方法中进行调用。
好处是: 大大减少程序中逻辑判断的分支。
规则3: 多播委托(“委托链”): 委托可以通过不断的进行“注册方法”,实现一次性调用多个方法。
规则4:在方法中调用委托,比方法中直接调用方法的好处是:
可以在不修改调用方法的前提下,不断的增加业务功能。
以此可以更加灵活的处理逻辑,减少逻辑判断。
规则5: 委托技术可以实现“调用方”与“实现方”的解耦合。
规则6: 可以通过+= 与-= 来给“多播委托”(委托链),来
动态的添加注册方法与取消注册方法。
规则7: 直接用“=”赋值符号,则仅最后注册的方法有效。
则就不是“多播委托”(或者说“委托链”)了,可以起到“阻止”的作用。
规则8: 委托本质就是方法的地址。(类似C++ 语言中的“函数指针”)
现在给出一个“事件”的示例,然后总结一下委托与事件的区别:
class UseEvent
{
//声明委托类型
//InformInfoHandler myInform;
//声明事件
public event InformInfoHandler eveMyInform;
public UseEvent()
{
//事件的注册()
//eveMyInform += new InformInfoHandler(InformMrZhangSan);
//eveMyInform += new InformInfoHandler(InformMrLisi);
//eveMyInform += new InformInfoHandler(InformMrWangWu);
//eveMyInform += new InformInfoHandler(InformMrNewComer);
//简化写法
eveMyInform += InformMrZhangSan;
eveMyInform += InformMrLisi;
eveMyInform += InformMrWangWu;
eveMyInform += InformMrNewComer;
}
public void InformMrZhangSan(string name)
{
Console.WriteLine("{0},老板来了,张三,请关闭电脑", name);
}
public void InformMrLisi(string name)
{
Console.WriteLine("{0},老板来了,李四,请关闭电脑", name);
}
public void InformMrWangWu(string name)
{
Console.WriteLine("{0},老板来了,王五,请关闭电脑", name);
}
//新来的同事
public void InformMrNewComer(string name)
{
Console.WriteLine("{0},老板来了,新来的同事,请关闭电脑", name);
}
/// <summary>
/// (前台)小张发信息
/// </summary>
public void MrZhangSendInfo()
{
eveMyInform("我是前台的小张");
}
static void Main1(string[] args)
{
Console.WriteLine("UseEvent 测试");
UseEvent obj = new UseEvent();
obj.MrZhangSendInfo();
}
}
按照以上示例,我们总结“委托”与“事件"的区别:
A) “事件”本质上就是委托的一个实例
B) 委托可以定义在类的外部,事件只能定义在类的内部。
以上代码是事件“调用方”与“功能实现方”都定义在了同一个类中,是不具备“实战”价值的,所以笔者给出如下在商业项目中应用的示例,供大家参考:
/// <summary>
/// 委托: 通知信息
/// </summary>
/// <param name="name"></param>
public delegate void InformInfoHandler(string name);
//调用方
class InvokeClass
{
//声明事件
public static event InformInfoHandler eveMyInform;
/// <summary>
/// 显示通知信息
/// </summary>
public void DisplayInformInfo()
{
if (eveMyInform!=null)
{
//eveMyInform("我是前台");
eveMyInform.Invoke("我是前台");
}
}
}
//业务功能实现方,以及“事件注册”
public class RegisterClass
{
public RegisterClass()
{
InvokeClass.eveMyInform += InfomrZhangSan;
InvokeClass.eveMyInform += InfomrLisi;
}
void InfomrZhangSan(string name)
{
Console.WriteLine("{0},张三,上午好",name);
}
void InfomrLisi(string name)
{
Console.WriteLine("{0},李四,下午好", name);
}
}
//测试类
class TestClass
{
/// <summary>
/// 测试方法
/// </summary>
public static void Main()
{
//注册事件
new RegisterClass();
//调用方
InvokeClass obj = new InvokeClass();
obj.DisplayInformInfo();
}
}
好了,以上代码大家如果可以看懂,就可以认为基本“懂”了委托与事件的基本使用,现在我们再引申性总结一下:
什么是“观察者模式”(Observer)
观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
这个委托与事件,笔者引申一点,帮助大家进一步思考:
我们在开发一个软件系统(游戏项目)时,常常要求在某一个对象的状态发生变化的时候,某些其它的对象做出相应的改变。例如: 我们游戏开发中,击中一架敌机,则屏幕UI的相应分数,要更新数值。做到这一点的设计方案有很多,但是为了使系统能够易于复用,应该选择低耦合度的设计方案。减少对象之间的耦合有利于系统的复用,并且这些低耦合的对象之间需要维持行动协调一致,保证很好的协作性。这样“观察者模式”是满足这一要求的最佳设计方案之一。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。