C#中的委托和事件详解

从大学就开始做C#这块,也做C#几年了,最近又从ios转回.Net,继续做C#,之前也没有写博客的习惯,写博客也是从我做ios的时候开始的,现在既然又做回了.net,那就写点关于.Net的博客,可能在大牛眼里这些都是简单基础的,不过回过头看我当时初学的时候觉得委托事件是不容易理解的,我这里也是想着联系着OC,两者有比较的学习下。毕竟都是面向对象语言,思想是相通的。

委托在OC中类似block,都是指向一个函数,其实他没和C++的函数指针类似。但委托还是和函数指针不太一样,委托是完全面向对象的,是类型安全可靠的。C++的指针仅仅指向成员函数,而委托同时封装了一个对象实例和方法。

委托声明用于定义一个从System.Delegate类派生的类。

格式:属性集 修饰符 delegate 返回值类型(A) 标识符(C)(形参列表(B));

一、委托是什么?

看上面的红字我们可以明白其实委托是一个类。其实类是什么?类也是一种数据类型,它了String类一样,也是一个数据类型,所以呢委托其实也是一个数据类型,只是这个数据类型和其他的有点不同,它这个数据类型指向的是一个函数。一个返回值为A,形参列表为B的名为标识符C的函数。其实这和OC中的block类似,block中也是用来定义函数。我们用typedef void(^myblock1)(int a,int b);来定义一个block,其实就是定义一个数据类型。上面的委托声明也是定义了一个引用类型的数据类型。

二、委托怎么用?

上面也说了,声明一个委托其实就是声明了一个数据类型,和Person、String一样都是一个数据类型。我们在使用委托和使用Person、String类型的数据一样。也是先声明:public 类型(Person、String) 变量(或属性)名。所以我们在使用委托时也是这样。只是这个变量或属性对应的是一个函数。

三、例子

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    //定义了一个从System.Delegate类派生的类
    //也可以理解为一种数据类型 这种数据类型指向返回值为void 参数为Person对象的函数
    //我们也可以把Person类理解为一种数据类型 只是它包含的是Name和Age
    public delegate void EatFood(Person p);

    public class Person
    {

        public string Name { get; set; }

        public int Age { get; set; }

        public Person(string name, int age)
        {
            Name = name;
            Age = age;
        }

        //既然委托是一数据类型和String一样,所以可以像声明String对象一样声明代理变量
        public EatFood eatFood;

        public void eating()
        {
            if (eatFood != null)
            {
                eatFood(this);
            }
        }

    }
}

上面定义了一个Person类,也定义了一个定义了一个从System.Delegate类派生的类EatFood,同时在Person类中声明了EatFood类类型的一个变量,在eating()函数中使用了这个变量。ps:请留意上面代码中的注释。下面的代码中我们定义了两个Person对象,一个chinesePerson一个englishPerson,而分别为两个类的eatFood变量指定不同的函数。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    class Program
    {
        static void Main(string[] args)
        {
            Person chinesePerson = new Person("小明",25);
            //通过构造函数实例化对象
            chinesePerson.eatFood = new EatFood(chineseEat);
            chinesePerson.eating();

            Console.WriteLine("--------------------------------------");

            Person englishPerson = new Person("Ivan",25);
            //通过直接复制来实例化对象
            englishPerson.eatFood = englishEat;
            englishPerson.eating();
            Console.ReadLine();

        }
        static void chineseEat(Person p)
        {
            Console.WriteLine("我是{0},我今年{1}岁了,我吃馒头",p.Name,p.Age);
        }
        static void englishEat(Person p)
        {
            Console.WriteLine("I'm {0},I am {1} , I eat MianBao",p.Name,p.Age);
        }
    }
}

可以看到针对不同的对象指定不同的eatFood变量则执行的结果也不一样。

四、委托和其他数据类型的区别

上面也说了可以把委托当做是一个数据类型,但它和普通的数据类型还是有区别的。这可能就是现在的个性吧,委托也有委托的个性。

委托实例化用于创建委托实例,和类实例创建语法相同。但委托可以封装多个方法,这些方法的集合合称为调用列表。委托使用+、+=、-、-=运算符向调用列表中增加或删除方法。

我们对上面的代码稍作改动,Person类不用改,只改Main方法中的。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    class Program
    {
        static void Main(string[] args)
        {
            Person chinesePerson = new Person("小明",25);
            //通过构造函数实例化对象
            chinesePerson.eatFood = new EatFood(chineseEat);
            chinesePerson.eatFood += englishEat;
            chinesePerson.eating();

            Console.WriteLine("--------------------------------------");

            Person englishPerson = new Person("Ivan",25);
            //通过直接复制来实例化对象
            englishPerson.eatFood = englishEat;
            englishPerson.eatFood += chineseEat;
            englishPerson.eating();
            Console.ReadLine();

        }
        static void chineseEat(Person p)
        {
            Console.WriteLine("我是{0},我今年{1}岁了,我吃馒头",p.Name,p.Age);
        }
        static void englishEat(Person p)
        {
            Console.WriteLine("I'm {0},I am {1} , I eat MianBao",p.Name,p.Age);
        }
    }
}

为了促进中西交流,好多人去留学也有好多来到中国的,所以在吃的方面彼此都会吃对方的。所以要增加方法来表示吃不同的食物。有了委托可以通过+=、-=来实现增加、删除调用列表,这样就方面很多。从下面的输出结果能看到,每个Person对象都调用了chineseEat、englishEat函数。

五、好处

上面的demo也展示了委托的使用方法,通过上面的使用我们可以思考下使用它的好处。我们如果不使用委托来实现这个功能的话,我们可能会在Person类中做一个判断,判断下是Chinses还是English,可是这样的话,如果哪天有了日本、法国等,那又要多好多个判断。可扩展性不好。可能有的会说可以在Person里面定义一个虚方法,分别声明Chinese、English类继承Person类重写虚方法,这确实是一个方法,如果有新的要扩展的话可以直接创建一个新的类重写虚方法就搞定了,不过这样的话如果只是这个方法不同,就要写一个类,这样未免杀鸡用牛刀了。所以说委托还是一个不错的选择。如果不仅要增加语言还要增加方法那这就更麻烦了。有了委托这些全解决。

六、事件

对象之间的交互是通过消息传递来实现的,而事件就是对象发送的消息,通过发信号的形式通知操作的发生。引发事件的对象为事件发送方,捕获事件并对其做出响应的对象为事件接收方。在事件通信中,事件发送方不知哪个对象或方法将接收它引发的事件,所需要的是在发送方和接收方之间用一个纽带来联系,在C#中使用委托为这个纽带。

事件声明的格式:属性集 修饰符 event 委托类型 事件名。

其实说白了就是事件是对委托变量的封装。请注意上面写的,我一直写的是委托类型的变量,面向对象的三大特征之一就是封装,例如变量和属性。在上面直接使用委托来指定函数,其实这和直接使用变量一样,但是在面向对象中一般不会直接访问变量,而是对变量进行封装,例如属性{get;set;}方法。事件是对委托的封装。我们来看一下事件的使用,和上面使用委托一样,我们在Person类中声明一个事件。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    //定义了一个从System.Delegate类派生的类
    //也可以理解为一种数据类型 这种数据类型指向返回值为void 参数为Person对象的函数
    //我们也可以把Person类理解为一种数据类型 只是它包含的是Name和Age
    public delegate void EatFoodDelegate(Person p);

    public class Person
    {

        public string Name { get; set; }

        public int Age { get; set; }

        public Person(string name, int age)
        {
            Name = name;
            Age = age;
        }

        //既然委托是一数据类型和String一样,所以可以像声明String对象一样声明代理变量
        //public EatFood eatFood;
        //之前是直接声明委托,现在是声明一个事件
        public event EatFoodDelegate EatFoodEventHandler;

        public void eating()
        {

            if (EatFoodEventHandler != null)
            {
                EatFoodEventHandler(this);
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegate
{
    class Program
    {
        static void Main(string[] args)
        {
            Person chinesePerson = new Person("小明",25);
            //通过构造函数实例化对象
            chinesePerson.EatFoodEventHandler += new EatFoodDelegate(chineseEat);
            chinesePerson.EatFoodEventHandler += englishEat;
            chinesePerson.eating();

            Console.WriteLine("--------------------------------------");

            Person englishPerson = new Person("Ivan", 25);
            //在委托中 可以直接使用=来给委托对象复制 而在事件中就不能直接使用= 要使用+=
            englishPerson.EatFoodEventHandler += new EatFoodDelegate(englishEat);
            englishPerson.EatFoodEventHandler += chineseEat;
            englishPerson.eating();
            Console.ReadLine();

        }
        static void chineseEat(Person p)
        {
            Console.WriteLine("我是{0},我今年{1}岁了,我吃馒头",p.Name,p.Age);
        }
        static void englishEat(Person p)
        {
            Console.WriteLine("I'm {0},I am {1} , I eat MianBao",p.Name,p.Age);
        }
    }
}

上面可以看到,使用事件来实现了同样的功能。

七、委托和代理设计模式的区别

不管是使用委托或者事件其实它们都是在A对象(本例中的Person对象)中调用B对象中的方法,这与设计模式中有相似之处。具体代理设计模式这里就省略了,委托和代理都是在A对象使用B对象中的方法。不过它们还是有区别的,委托中在A中直接使用的是B中的方法,是类与方法之间的,代理设计模式中是将A类中设置一个B类变量,然后通过B来使用B中的方法,是类与类之间的。这也是我的个人理解,不知道对不对,错了的话也希望大牛指正,以免耽误了其他的社会主义接班人。

到此这篇关于C#委托和事件的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 一篇文章彻底搞清楚c#中的委托与事件

    一.什么是委托呢? 听着名字挺抽象,确实不好理解.面试官最喜欢考察这个,而且更喜欢问:"委托和事件有何异同?".如果对一些知识点没有想明白,那么很容易被绕进去.研究任何事物,我们不妨从它的定义开始,委托也不例外.那么先来看c#中的委托定义,先来个例子: public delegate void GetPacage(string code); 这个委托,看起来就是个方法签名,取包裹,需要验证码.与方法签名不同的地方,在于多了一个delegate.c#中不乏一些便利好用的语法,比如fore

  • 浅谈C#中的委托、事件与异步

    从刚接触c#编程到现在,差不多快有一年的时间了.在学习过程中,有很多地方始终似是而非,直到最近才弄明白. 本文将先介绍用法,后评断功能. 一.委托 基本用法: 1.声明一个委托类型.委托就像是'类'一样,声明了一种委托之后就可以创建多个具有此种特征的委托.(特征,指的是返回值.参数类型) public delegate void SomeKindOfDelegate(string result); 2.创建一个在1中创建的委托类型的委托. public SomeKindOfDelegate aD

  • C#事件标准命名规则及说明(包括用作事件类型的委托命名)

    目录 C#事件标准命名规则 事件的名称 事件触发方法的名称 C#命名规范.变量.类Class C#中的命名规范 什么是变量 类Class 总结 C#事件标准命名规则 一些开源代码的事件命名很混乱,以此文章用作本人以后工作的参考. 事件的名称 事件始终是指某个操作,这个操作可能正在发生,也可能已经发生. 因此与方法一样,事件用谓词命名,谓词时态用于指示事件引发的时间. ✓ 务必使用谓词或谓词短语来命名事件. 示例:Clicked.Painting.DroppedDown 等. ✓ 务必通过使用现在

  • C#中的委托和事件详解

    从大学就开始做C#这块,也做C#几年了,最近又从ios转回.Net,继续做C#,之前也没有写博客的习惯,写博客也是从我做ios的时候开始的,现在既然又做回了.net,那就写点关于.Net的博客,可能在大牛眼里这些都是简单基础的,不过回过头看我当时初学的时候觉得委托事件是不容易理解的,我这里也是想着联系着OC,两者有比较的学习下.毕竟都是面向对象语言,思想是相通的. 委托在OC中类似block,都是指向一个函数,其实他没和C++的函数指针类似.但委托还是和函数指针不太一样,委托是完全面向对象的,是

  • js中鼠标滚轮事件详解(firefox多浏览器)

    附加事件 其中经我测试,IE/Opera属于同一类型,使用attachEvent即可添加滚轮事件. 复制代码 代码如下: /*IE注册事件*/ if(document.attachEvent){ document.attachEvent('onmousewheel',scrollFunc); } Firefox使用addEventListener添加滚轮事件 复制代码 代码如下: /*Firefox注册事件*/ if(document.addEventListener){ document.ad

  • javascript移动开发中touch触摸事件详解

    事件对象是用来记录一些事件发生时的相关信息的对象.事件对象只有事件发生时才会产生,并且只能是事件处理函数内部访问,在所有事件处理函数运行结束后,事件对象就被销毁! W3C DOM把事件对象作为事件处理函数的第一个参数传入进去 IE将事件对象作为window对象的一个属性(相当于全局变量) originalEvent对象 在一次偶然的使用中,我发现当使用on()函数并且传入第二个选择器参数时,e.touches[0]的访问为undefined,打印e发现,它的事件对象不是原生的事件对象.经查阅发现

  • c# 实现控件(ocx)中的事件详解

    c#控件实现类似c++中ocx控件功能 c++中ocx控件 1.控件方法 2.控件事件 c#很容易实现c++中ocx中控件方法的功能,但是实现类似c++中ocx的控件事件,则需要一定的周折. 下面就用实例简单的介绍c#如何实现 c#中ActiveX(ocx)实现实例(vs2008环境下): using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using

  • WPF中鼠标/键盘/拖拽事件以及用行为封装事件详解

    目录 鼠标事件 键盘输入事件 拖拽事件 用行为封装事件 用事件来实现 用行为来封装 本文主要介绍了WPF中常用的鼠标事件.键盘事件以及注意事项,同时使用一个案例讲解了拓展事件.除此之外,本文还讲述如何用行为(Behavior)来封装事件. Windows中的事件通过消息机制来完成,也就是Windows系统来捕获用户输入(如鼠标点击.键盘输入),然后Windows发送一个消息给应用程序,应用程序进行具体的处理.在Winform中,窗体中每个控件都是有独立的句柄,也就是每个控件都可以收到Window

  • 基于angular中的重要指令详解($eval,$parse和$compile)

    在angular的服务中,有一些服务你不得不去了解,因为他可以说是ng的核心,而今天,我要介绍的就是ng的两个核心服务,$parse和$compile.其实这两个服务讲的人已经很多了,但是100个读者就有100个哈姆雷特,我在这里讲讲自己对于他们两个服务的理解. 大家可能会疑问,$eval呢,其实他并不是一个服务,他是scope里面的一个方法,并不能算服务,而且它也基于parse的,所以只能算是$parse的另一种写法而已,我们看一下ng源码中$eval的定义是怎样的就知道了 $eval: fu

  • python中 logging的使用详解

    日志是用来记录程序在运行过程中发生的状况,在程序开发过程中添加日志模块能够帮助我们了解程序运行过程中发生了哪些事件,这些事件也有轻重之分. 根据事件的轻重可分为以下几个级别: DEBUG: 详细信息,通常仅在诊断问题时才受到关注.整数level=10 INFO: 确认程序按预期工作.整数level=20 WARNING:出现了异常,但是不影响正常工作.整数level=30 ERROR:由于某些原因,程序 不能执行某些功能.整数level=40 CRITICAL:严重的错误,导致程序不能运行.整数

  • jquery html动态添加的元素绑定事件详解

    在实际开发中会遇到要给动态生成的html元素绑定触发事件的情况: <div id="testdiv"> <ul></ul> </div> 假设我们要给ul动态添加的<li>绑定click事件形成如下结果 <div id="testdiv"> <ul> <li name="apple">apple</li> <li name="

  • Angular中的$watch方法详解

    在$apply方法中提到过脏检查,首先apply方法会触发evel方法,当evel方法解析成功后,会去触发digest方法,digest方法会触发watch方法. (1)$watch简介 在digest执行时,如果watch观察的的value与上一次执行时不一样时,就会被触发. AngularJS内部的watch实现了页面随model的及时更新. $watch方法在用的时候主要是手动的监听一个对象,但对象发生变化时触发某个事件. (2)watch方法用法 $watch(watchFn,watch

  • Vue自定义事件(详解)

    前面的话 父组件使用props传递数据给子组件,子组件怎么跟父组件通信呢?这时,Vue的自定义事件就派上用场了.本文将详细介绍Vue自定义事件 事件绑定 每个 Vue 实例都实现了事件接口 (Events interface),即 使用 $on(eventName) 监听事件 使用 $emit(eventName) 触发事件 [注意]Vue 的事件系统分离自浏览器的EventTarget API.尽管它们的运行类似,但是 $on 和 $emit 不是addEventListener 和 disp

随机推荐