从大学就开始作C#这块,也作C#几年了,最近又从ios转回.Net,继续作C#,以前也没有写博客的习惯,写博客也是从我作ios的时候开始的,如今既然又作回了.net,那就写点关于.Net的博客,可能在大牛眼里这些都是简单基础的,不过回过头看我当时初学的时候以为委托事件是不容易理解的,我这里也是想着联系着OC,二者有比较的学习下。毕竟都是面向对象语言,思想是相通的。ios
委托在OC中相似block,都是指向一个函数,其实他没和C++的函数指针相似。但委托仍是和函数指针不太同样,委托是彻底面向对象的,是类型安全可靠的。C++的指针仅仅指向成员函数,而委托同时封装了一个对象实例和方法。设计模式
委托声明用于定义一个从System.Delegate类派生的类。安全
格式:属性集 修饰符 delegate 返回值类型(A) 标识符(C)(形参列表(B));函数
1、委托是什么?学习
看上面的红字咱们能够明白其实委托是一个类。其实类是什么?类也是一种数据类型,它了String类同样,也是一个数据类型,因此呢委托其实也是一个数据类型,只是这个数据类型和其余的有点不一样,它这个数据类型指向的是一个函数。一个返回值为A,形参列表为B的名为标识符C的函数。其实这和OC中的block相似,block中也是用来定义函数。咱们用typedef void(^myblock1)(int a,int b);来定义一个block,其实就是定义一个数据类型。上面的委托声明也是定义了一个引用类型的数据类型。this
2、委托怎么用?spa
上面也说了,声明一个委托其实就是声明了一个数据类型,和Person、String同样都是一个数据类型。咱们在使用委托和使用Person、String类型的数据同样。也是先声明:public 类型(Person、String) 变量(或属性)名。因此咱们在使用委托时也是这样。只是这个变量或属性对应的是一个函数。.net
3、例子设计
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变量则执行的结果也不同。
4、委托和其余数据类型的区别
上面也说了能够把委托当作是一个数据类型,但它和普通的数据类型仍是有区别的。这可能就是如今的个性吧,委托也有委托的个性。
委托实例化用于建立委托实例,和类实例建立语法相同。但委托能够封装多个方法,这些方法的集合合称为调用列表。委托使用+、+=、-、-=运算符向调用列表中增长或删除方法。
咱们对上面的代码稍做改动,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函数。
5、好处
上面的demo也展现了委托的使用方法,经过上面的使用咱们能够思考下使用它的好处。咱们若是不使用委托来实现这个功能的话,咱们可能会在Person类中作一个判断,判断下是Chinses仍是English,但是这样的话,若是哪天有了日本、法国等,那又要多好多个判断。可扩展性很差。可能有的会说能够在Person里面定义一个虚方法,分别声明Chinese、English类继承Person类重写虚方法,这确实是一个方法,若是有新的要扩展的话能够直接建立一个新的类重写虚方法就搞定了,不过这样的话若是只是这个方法不一样,就要写一个类,这样未免杀鸡用牛刀了。因此说委托仍是一个不错的选择。若是不只要增长语言还要增长方法那这就更麻烦了。有了委托这些全解决。
6、事件
对象之间的交互是经过消息传递来实现的,而事件就是对象发送的消息,经过发信号的形式通知操做的发生。引起事件的对象为事件发送方,捕获事件并对其作出响应的对象为事件接收方。在事件通讯中,事件发送方不知哪一个对象或方法将接收它引起的事件,所须要的是在发送方和接收方之间用一个纽带来联系,在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); } } }
上面能够看到,使用事件来实现了一样的功能。
7、委托和代理设计模式的区别
不论是使用委托或者事件其实它们都是在A对象(本例中的Person对象)中调用B对象中的方法,这与设计模式中有类似之处。具体代理设计模式这里就省略了,委托和代理都是在A对象使用B对象中的方法。不过它们仍是有区别的,委托中在A中直接使用的是B中的方法,是类与方法之间的,代理设计模式中是将A类中设置一个B类变量,而后经过B来使用B中的方法,是类与类之间的。这也是个人我的理解,不知道对不对,错了的话也但愿大牛指正,以避免耽误了其余的社会主义接班人。