什么是委托?为什么要用委托?委托有那几种形式?怎么使用委托?
委托的定义:
委托是一种特殊类型的对象(也就是说,可以看作是一个特殊的类),包含一个或者多个方法的地址。
为什么要用委托:
当需要把一个方法进行传递时就需要用到委托来实现,在C/C++的时候是提取函数地址的指针进行传递的,但是这样是没有安全性的,因为你无法对其进行安全性校验,这就造成了编码的不可控性,非法的数据就可能被调用。在.NET Framework中就加入了委托的概念,如果要传递方法,就必须把方法的细节封装在一个新型的对象中–委托。所以委托是针对方法进行的操作,委托对数据是什么并不关心(也是面向对象编程思路,只关心自己要处理的)。
委托有几种形式:
1,自定义委托
2,泛型委托:Action,Func,自定义泛型委托
3,匿名委托
4,多播委托
5,Lambda形式的委托
从返回形式来看,委托可以分为两种。一种是有返回值的委托,一种是没有返回值的委托(可以成为事件委托)。这两种形式的委托定义,声明和委托绑定还是有区别的。如下做简要展示。
//定义有返回值的委托
pubilc delegate string GenericDelegate<T,S>(T title, S author);
//定义无返回值的事件委托
public delegate void GenericDelegateEvent<E,P>(E Name, P Address);
public class GenericDelegateClass<V,F>
{
//声明委托
GenericDelegate<V,F> GDeleValue;
//声明事件委托
GenericDelegateEvent<V,F> GDeleEvent = null;
}
//绑定委托
pubilc void Click(object sender , EventArgs e)
{
GenericDelegateClass<string ,string> gdc = new GenericDelegateClass<string ,string>();
//绑定委托,可以用=。
gdc.GDeleValue = new GenericDelegate<string,string>(Delegate);
//绑定事件委托,事件委托只能用+=,-=。
gdc.GDeleEvent += new GenericDelegateEvent<stirng,string>(EventDelegate);
public string Delegate<T,S>(T title,S author)
{
return title.ToString() + author;
}
private void EventDelegate<V, F>(V name, F address)
{
//
}
}
下面举个例子来帮助理解委托的工作方式,这也是面向对象编程的思维方式。
故事里面有三个要素:任务下达,你的组长,你。
今天公司给你们组下达了一个开发任务,你组长接受到任务之后觉得你适合完成这个任务,就将任务委托给了你去实现,你做完之后把结果给你组长。
其实委托的实现流程和上面是一样的(所以叫委托= =)
怎么使用委托
下面用事件来阐述如何使用委托
委托的定义语法如下:
delegate void IntMethodInvoker(int x);
关键词delegate 表示这是个委托对象,void和int参数表示该委托包含的方法带有一个int型的参数且返回值是void。这也体现了委托的安全性。(为什么委托的返回类型要和调用的方法的返回类型一样,一方面是为了保证委托的安全性,另一方面方法的处理结果要靠委托表达出来,所以要一样)
委托的实例化
委托的实例化和普通类的实例化是一样
IntMethodInvoker invoker = new IntMethodInvoker( );
代码下载地址:http://download.csdn.net/download/geshicuowu/9933392
下面举个最简单的例子来展现委托
很明显结果会返回:40
但是上面的这个例子显然没有说到点上,委托的使用看起来和直接使用x.ToString( )方法没有什么特别的优势。(在后面会举一个更能说明委托作用的一个例子。该例子主要是为了表明委托的基本形式,以后的形式都是在此上面的扩展)
泛型委托
其实在实际的项目中,泛型委托的形式更为常见。其中action委托表示引用一个返回void类型的方法,参数范围是0~16个。func委托可以引用具有返回类型的方法。例如func(in T1,out TResult)
下面将举一个泛型委托的例子,也更能体现出委托的特点。(例子来源于c#高级编程)
1:定义一个排序类,里面声明一个static方法,其中一个参数为泛型委托func
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 这个类时用来演示泛型委托的
/// 冒泡排序
/// </summary>
namespace DelegateExample
{
class BubbleSort
{
/// <summary>
/// 冒泡排序方法
/// </summary>
/// <typeparam name="T">泛型参数</typeparam>
/// <param name="sortArry">传入的排序队列</param>
/// <param name="comparison">泛型委托</param>
/// <returns></returns>
static public bool Sort<T>(IList<T> sortArry, Func<T, T, bool> comparison)
{
for (int j = 0; j < sortArry.Count; j++)
{
for (int i = j+1; i < sortArry.Count; i++)
{
if (comparison(sortArry[j], sortArry[i]))
{
var temp = sortArry[j];
sortArry[j] = sortArry[i];
sortArry[i] = temp;
}
}
}
return false;
}
}
}
2:定义一个employee类,其中有工资的比较方法,和上面的泛型委托的参数和返回类型相匹配。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 自定义的一个员工类
/// 用来示范泛型委托的使用
/// author:zhangzhen
/// date: 2017/08/14
/// </summary>
namespace DelegateExample
{
class Employee
{
public Employee(string name, double salery)
{
Name = name;
Salery = salery;
}
public string Name
{
get;
private set;
}
public double Salery
{
get;
private set;
}
/// <summary>
/// 比较薪资的函数
/// </summary>
/// <param name="e1"></param>
/// <param name="e2"></param>
/// <returns></returns>
static public bool compare(Employee e1,Employee e2)
{
return e1.Salery < e2.Salery;
}
}
}
3:最后时实现类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*******************************************
* 这个控制台程序主要用来演示委托delegate的相关知识
* author:zhangzhen
* date :2017-08-10
* ******************************************/
namespace DelegateExample
{
class Program
{ //定义一个委托
delegate string GetStringInvoker();
static void Main(string[] args)
{
Employee[] employees =
{
new Employee("zhangzhen",10000000),
new Employee("xiaoming",10000),
new Employee("xiaohuang",20000),
new Employee("xiaodong",8000)
};
BubbleSort.Sort(employees, Employee.compare);
foreach(var item in employees)
{
Console.WriteLine(item.Name + " /// "+ item.Salery);
}
//让控制台暂停等待输入。
Console.ReadKey();
}
}
}
输出结果符合预期。
也就是说任何对象(只要该对象中有比较方法,且比较方法的形式符合BubbleSort类中Sort方法中的泛型委托的要求的)都可以使用Sort方法实现冒泡排序。试想,如果没有委托(尤其是泛型委托)的话,这个效果时很难实现的。
多播委托
上面那个排序的例子中,调用一次委托就调用一次方法,如果要调用多个方法,就要多次显示的调用这个委托,使用起来不是很方便,多播委托就比较好的解决了这个问题。
多播委托可以委托多个方法,在调用多播委托时,将按顺序连续调用多个方法。(因为不可能返回多个返回类型,因此多播委托的签名必须是void)多播委托可以使用-=,+=来向委托列表中删减方法或者增加方法。
使用多播委托时的注意点:
1:多播委托并不会严格的按照委托列表中的顺序来执行,也就是说不能把有顺序特定依赖的方法加到委托列表中。
2:多播委托中,如果委托链表中一个方法发生了异常,整个迭代就会终止。为了避免这个问题,Delegate类中定义了GetInvocationList()方法,它返回一个Delegate对象数组,可以利用这个委托调用与委托列表中相关的方法,同时捕获异常,并不会终止迭代。
在一些涉及业务较为繁杂的项目中,泛型委托和多播委托一般都会结合使用。
下面就是一个比较简单的例子(因为多播委托中委托的方法返回类型是void,所以逻辑处理基本都是在被委托的方法中实现)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 这个类是用来演示多播委托的一个辅助类
/// </summary>
namespace DelegateExample
{
class MultiDelegate
{
static public void One()
{
Console.WriteLine("这是方法ONE");
}
static public void Two()
{
Console.WriteLine("这是方法Two");
}
}
}
下面是实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*******************************************
* 这个控制台程序主要用来演示委托delegate的相关知识
* author:zhangzhen
* date :2017-08-10
* ******************************************/
namespace DelegateExample
{
class Program
{
/*********************多播委托的演示**********************************/
Action action = MultiDelegate.One;
action += MultiDelegate.Two;
action += MultiDelegate.One;
//将Two方法从委托链表中移除
//action -= MultiDelegate.Two;
Delegate[] tempDelegate = action.GetInvocationList();
//下面的形式是不可行的,var没办法调用委托中的方法,必须用Action来修饰item
//foreach (var item in tempDelegate)
//{
// item();
//}
foreach (Action item in tempDelegate)
{
item();
}
//让控制台暂停等待输入。
Console.ReadKey();
}
}
}