C#forUnity快速入门(连载15)_C#委托与事件

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();

}
    }


    好了,以上代码大家如果可以看懂,就可以认为基本“懂”了委托与事件的基本使用,大家有什么问题,欢迎讨论。

时间: 08-02

C#forUnity快速入门(连载15)_C#委托与事件的相关文章

C#forUnity快速入门(连载11)-C#的属性

C# for Unity编程语言快速入门教程(连载11)_C#的属性    C#的"属性"是一种类字段的约束控制手段,其直接目的就是控制字段的输入合法性,以及实现对字段的快速访问. 目的:     引入属性概念是为了更合理的控制对字段的可访问性.    规则1: 属性是方法的简化版,主要是用于对字段的控制.规则2: 属性本质就是Set 与Get 方法的简化.规则3: 属性可以写成只读属性.只写属性.规则4: 属性中如果不需要对字段做控制处理,可以简化写法. 因为这个概念相对比较简单,所

C#forUnity快速入门(连载12)-C#的字符串

C# for Unity编程语言快速入门教程(连载12)_C#的字符串 "C#字符串"是一个重要的知识点,对于C#初学者来说有很多重要知识点需要进行学习,总体归纳有三个大的方面: 知识点一:  字符串的常用方法与属性 属性:     Length:           得到字符串的长度   方法:    IsNullOrEmpty()   表示空字符串: IndexOf()  :           查找指定字符(子字符串). SubString():          字符串截取(得

C#forUnity快速入门(连载13)-C#结构体

C# for Unity编程语言快速入门教程(连载13)_C#结构体 C#的"结构体"使用Struct 关键字来定义,是与"类"."接口"并列的编程单位.其设计的主要目的是自定义"值类型",即允许用户自定义值类型. 适用范围:   结构适合一些小型的数据结构,这些结构包含的数据以创建后不修改的数据为主.也适合数据打包,一次性定义多个变量. 结构体的限制很多.1> 除非字段声明为const .static,否则无法初始化.

C# for Unity快速入门(连载14)_C#枚举类型

C# for Unity编程语言快速入门教程(连载14)_C#枚举类型 C#的枚举类型(用 "enum"关键字标识),按照笔者理解本质上是一个隐式继承了System.Enum的整形类.枚举类型的主要作用是:为了更好的识别一个变量的具体含义,且固定变量的使用"范围". 例如:形如如下定义 public enum Week{   Monday,  Tuesday,  Wenesday,  Thursday,  Friday,  Saterday,  Sunday }  

AngularJS快速入门指南15:API

thead>tr>th, table.reference>tbody>tr>th, table.reference>tfoot>tr>th, table.reference>thead>tr>td, table.reference>tbody>tr>td, table.reference>tfoot>tr>td { padding: 8px; line-height: 1.42857143; vertic

WPF快速入门系列(3)——深入解析WPF事件机制

一.引言 WPF除了创建了一个新的依赖属性系统之外,还用更高级的路由事件功能替换了普通的.NET事件. 路由事件是具有更强传播能力的事件——它可以在元素树上向上冒泡和向下隧道传播,并且沿着传播路径被事件处理程序处理.与依赖属性一样,可以使用传统的事件方式使用路由事件.尽管路由事件的使用方式与传统的事件一样,但是理解其工作原理还是相当重要的. 二.路由事件的详细介绍 对于.NET中的事件,大家应该在熟悉不过了.事件指的在某个事情发生时,由对象发送用于通知代码的消息.WPF中的路由事件允许事件可以被

AngularJS快速入门指南20:快速参考

thead>tr>th, table.reference>tbody>tr>th, table.reference>tfoot>tr>th, table.reference>thead>tr>td, table.reference>tbody>tr>td, table.reference>tfoot>tr>td { padding: 8px; line-height: 1.42857143; vertic

AngularJS快速入门指南14:数据验证

thead>tr>th, table.reference>tbody>tr>th, table.reference>tfoot>tr>th, table.reference>thead>tr>td, table.reference>tbody>tr>td, table.reference>tfoot>tr>td { padding: 8px; line-height: 1.42857143; vertic

AngularJS快速入门指南16:Bootstrap

thead>tr>th, table.reference>tbody>tr>th, table.reference>tfoot>tr>th, table.reference>thead>tr>td, table.reference>tbody>tr>td, table.reference>tfoot>tr>td { padding: 8px; line-height: 1.42857143; vertic