认识委托(Delegate)
我想关于委托,已经有数不清的好文章,来讲解他了,但是如果仅仅是看别人的东西难免记忆不深刻和不能透彻的理解,从入门到现在,几乎每天都在使用委托。但是自己从来没有清晰的了解过他。现在准备重新开始一点点的学习一下。本文也可以说是一篇读书笔记,因为大部分内容,学习自:《C#本质论》
Overview
什么是委托?
答: 字符串是string类型,小数是double或者float类型,那么委托就是方法的类型,他可以将方法作为参数进行传递。
如何定义一个委托
//可以根据自己的需要来定义返回值和参数public delegate 委托的返回值(可选否则为void) 委托的名字 (委托所需要的参数(可选));
委托的类型是 "方法" 类型, 不同的方法之间在签名上肯定会有一些细节的差别,当我么定义一个委托的时候,首先要思考一下,我的委托是为什么签名的方法来准备的呢?等思考完这一点,再着手去定义我们的委托,光说不练假把式,我们还是通过代码来举例子吧。
比如说,将下面的方法封装为一个委托,我们来看一下这个简陋的方法的签名
存在一个string 类型的参数 ,那么我们的委托也要提供一个 string 类型的参数 void 返回值 , 那么我们的委托的返回值也要是 void.public void SayMyName(string name){ Console.WriteLine("鲁迅认识的那只猹");}
将上面的这个方法,封装为委托就是
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace UnderstandDelegate{ delegate void SayMyNameHandler(string name);}
Note: 在定义委托的时候一定要注意,我们的委托的签名一定要和我们想要封装的方法的签名一定要一一对应.。
委托带来的好处
下面我们假设有这么一个场景,我们有一个Person集合,现在我们要从中筛选一些数据。
先建立好Person类
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace StudyDelegate{ public class Person { public string Name { get; set; } public string Gender { get; set; } public int Age { get; set; } public string IdNumber { get; set; } }}
Person集合
private static ListpersonList = new List { new Person() { Name="鲁迅认识的那只猹1", Age=18, Gender="Male", IdNumber="123"}, new Person() { Name="鲁迅认识的那只猹2", Age=19, Gender="男", IdNumber="123"}, new Person() { Name="鲁迅认识的那只猹3", Age=20, Gender="雄性", IdNumber="123"}, new Person() { Name="鲁迅认识的那只猹4", Age=30, Gender="Male", IdNumber="123"}, new Person() { Name="鲁迅认识的那只猹5", Age=10, Gender="Male", IdNumber="123"},};
- 我的需求: 现在我要从Person的集合中找到Name=
鲁迅认识的那只猹1
的 Person
public static Person FindPerson(){ foreach (var item in personList) { if (item.Name.Equals("鲁迅认识的那只猹")) ; return item; } throw new Exception("您所找的Person不存在!");}
上面的代码显然是没有问题的,那么我们来调用一下。
static void Main(string[] args){ Person person = FindPerson(); string info = string.Format("Name:{0}, Age ={1}, Gender={2} ,IDNumber={3}", person.Name, person.Age, person.Gender, person.IdNumber); Console.WriteLine(info);}//输出结果//Name:鲁迅认识的那只猹1, Age =18, Gender=Male ,IDNumber=123
总之,需求总是善变的,假设现在需求改变了:
- 从personList中查找 IDNumber=
1237
的 Person。
如果说,你想要把 上面的代码复制下来,修改修改完事,那么你真是一个糟糕的程序员, 需求总是变化的,万一那次我们的需求再次遇到的变化,难道要一直进行复制粘贴吗?
使用委托简化代码
Note:
public static Person FindPerson(){ foreach (var item in personList) { if (item.Name.Equals("鲁迅认识的那只猹")) return item; } throw new Exception("您所找的Person不存在!");}
首先我们要找到,代码中不断变化的部分,然后将其抽取为一个委托。
- 很显然,这个简单的例子中,if 语句包裹的部分,是会经常变化的。
那么,我们要做的就是将我们不断变化的不认抽取为一个委托
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace StudyDelegate{ ////// 查询Person集合的委托 /// ///public delegate bool SearchDelegate(Person obj);}
我想我们将我们的方法进行一下简单的修改:
public static Person FindPerson(SearchDelegate sd){ foreach (var item in personList) { //执行委托 if (sd(item)) return item; } throw new Exception("您所找的Person不存在!");}
调用
- 查询Name = 鲁迅认识的那只猹1 的person
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace StudyDelegate{ class Program { private static ListpersonList = new List { new Person() { Name="鲁迅认识的那只猹1", Age=18, Gender="Male", IdNumber="123"}, new Person() { Name="鲁迅认识的那只猹2", Age=19, Gender="男", IdNumber="1234"}, new Person() { Name="鲁迅认识的那只猹3", Age=20, Gender="雄性", IdNumber="1235"}, new Person() { Name="鲁迅认识的那只猹4", Age=30, Gender="Male", IdNumber="1236"}, new Person() { Name="鲁迅认识的那只猹5", Age=10, Gender="Male", IdNumber="1237"}, }; static void Main(string[] args) { Person person = FindPerson(NameEquals); string info = string.Format("Name:{0}, Age ={1}, Gender={2} ,IDNumber={3}", person.Name, person.Age, person.Gender, person.IdNumber); Console.WriteLine(info); } /// /// 判断Person Name 是否等于 鲁迅认识的那只猹1 /// /// ///public static bool NameEquals(Person person) { return person.Name.Equals("鲁迅认识的那只猹1"); } public static Person FindPerson(SearchDelegate sd) { foreach (var item in personList) { if (sd(item)) return item; } throw new Exception("您所找的Person不存在!"); } }}//输出结果://Name:鲁迅认识的那只猹1, Age =18, Gender=Male ,IDNumber=123
如果这时候,我们的需求再次遇到了变化的话,那么我们需要修改的就很少了。
- 查询 IdNumber =1234 的perosn。
这时候,我们只需要添加一个方法,在将调用的时候传递的委托参数进行修改即可:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace StudyDelegate{ class Program { private static ListpersonList = new List { new Person() { Name="鲁迅认识的那只猹1", Age=18, Gender="Male", IdNumber="123"}, new Person() { Name="鲁迅认识的那只猹2", Age=19, Gender="男", IdNumber="1234"}, new Person() { Name="鲁迅认识的那只猹3", Age=20, Gender="雄性", IdNumber="1235"}, new Person() { Name="鲁迅认识的那只猹4", Age=30, Gender="Male", IdNumber="1236"}, new Person() { Name="鲁迅认识的那只猹5", Age=10, Gender="Male", IdNumber="1237"}, }; static void Main(string[] args) { Person person = FindPerson(IdNumberEquals1234); string info = string.Format("Name:{0}, Age ={1}, Gender={2} ,IDNumber={3}", person.Name, person.Age, person.Gender, person.IdNumber); Console.WriteLine(info); } /// /// 判断Person Name 是否等于 鲁迅认识的那只猹1 /// /// ///public static bool NameEquals(Person person) { return person.Name.Equals("鲁迅认识的那只猹1"); } /// /// 判断 Person IDNumber 是否为 1234 /// /// ///public static bool IdNumberEquals1234(Person person) { return person.IdNumber.Equals("1234"); } public static Person FindPerson(SearchDelegate sd) { foreach (var item in personList) { if (sd(item)) return item; } throw new Exception("您所找的Person不存在!"); } }}
总结
有了委托,我们的程序更加的灵活,并且减少了很多多余的代码,关于委托,我们只需要记住: 将方法作为参数传递 这个概念即可。