说明:本文灵感来自直面曹大版主直击语言本质的震撼(http://bbs.youkuaiyun.com/topics/390622815),所以摘抄了不少版主的原话
委托
委托是C#提出的一套独有的机制,它封装了一种结构,能够将方法当作参数传入其他函数,它能让一个函数可以实现总体流程的复用,同时让调用者“填空”,自定义某个细节。而一般的函数,则只能复用功能本身。
比如我写了这样一个函数
private void Show(int numA,int numB)
{
Console.WriteLine(numA + numB);
}
它能够输出1+2的值,现在想要写计算器了,光输出一个加不够,现在最少还要减法,那么再写一个
private void Show_Subtract(int numA,int numB)
{
Console.WriteLine(numA - numB);
}
类似的还有乘法,除法,所以我要依次去写四种输出方法,如果以后还要再增加具体操作的话,再继续增加方法,比如开平方根,求幂等等。
这么多的同类型方法放在客户端被调用(对于客户端做展示而言,抽象成‘输出’操作)会显得非常难以管理维护以及不安全,在委托特性尚未发布之前,我们处理类似情况的时候也有其他多种途径,设计模式是个非常不错的选择,所以我们引入了一些设计模式来帮助我们管理这些操作方法,代码结构如下(设计模式不是主要讲的内容,感兴趣推荐一本书《大话设计模式》)。
在委托出现之前
提供一个统一的接口,开放给外部调用
abstract class OperationBase
{
//禁止直接给NumA,NumB赋值,由外部传递参数进来,确保封装的安全性
public int NumA { get; private set; }
public int NumB { get; private set; }
public OperationBase(int numA, int numB)
{
NumA = numA;
NumB = numB;
}
/// <summary>
/// 每一个计算器(操作)都需要返回值,由具体操作决定
/// </summary>
/// <returns></returns>
public abstract double GetResult();
}
为不同的操作去实现内容
加法操作
class Add : OperationBase
{
public Add(int numA, int numB)
: base(numA, numB)
{
}
public override double GetResult()
{
return NumA + NumB;
}
}
减法操作
class Subtract : OperationBase
{
public Subtract(int numA, int numB)
: base(numA, numB)
{ }
public override double GetResult()
{
return NumA - NumB;
}
}
具体调用:
private static void Show(OperationBase op)
{
//真正实现操作的不是op自己,而是实现其操作本身的实例
Console.WriteLine("利用设计模式来管理同一类型的操作,{0} {1} {2}={3}", op.NumA, op.GetType().Name, op.NumB, op.GetResult());
Console.ReadLine();
}
static void Main(string[] args)
{
//在这里传入了操作的实例对象
Show(new Add(1, 2));
}
运行后输出1 And 2 = 3
这样简单封装了一下之后,客户端的压力减轻了,只需要进行一个操作(输出),把其中的实现过程隐藏好了,这样我们直需要使用对象就可以能够实现,这更加符合面向对象设计。
而在委托出现之后,我们可以这样做:
//首先写好一些操作
class Operation
{
public double Add(int numA, int numB)
{
return numA + numB;
}
public double Decrease(int numA, int numB)
{
return numA - numB;
}
public double Multipy(int numA, int numB)
{
return numA * numB;
}
public double Divide(int numA, int numB)
{
if(numB == 0)
throw new InvalidOperationException();
return numA / numB;
}
}
接着就能直接在客户端调用
//首先声明委托
private delegate double OperationDelegate(int numA, int numB);
static void Main(string[] args)
{
OperationDelegate opd = new Operation().Add;
//在这里传入了操作的实例对象
Show(1, 2, opd);
//比较上面的委托方法,从表象看它只是把参数放在了外面传入,再传入一个delegate
Show(new Add(1, 2));
}
略改变输出方法:
private static void Show(int numA, int numB, OperationDelegate opd)
{
//真正实现操作的方法被当做参数传入了
Console.WriteLine("利用委托直接传入方法\n{0}{1}{2}={3}", numA, opd(numA, numB).GetType().Name, numB, opd(numA, numB));
Console.ReadLine();
}
整个过程中,我们省略了之前一大串为设计模式而准备的代码(内容),在这里的表象显示了它不用再为每个操作方法写一个类,也不用每次都再实例化类,一个操作(operation)实例,全局搞定实际操作(Add,Substract)。
而在匿名方法出现后,更加简化了委托调用方法的方式:
static void Main(string[] args)
{
OperationDelegate opd = new Operation().Add;
//在这里传入了操作的实例对象
Show(1, 2,opd);
//匿名函数的委托,轻小简便甚至不用声明方法体,但方法内容过长会降低可读性。
Show(1, 2, (x, y) => { return x + y; } );
//比较上面的委托方法,从表象看它只是把参数放在了外面传入,再传入一个delegate
Show(new Add(1, 2));
}
注意:我们没有声明新的Show方法,同样还是同一个方法,
当然,使用了用泛型参数我们更加能看明白:
private static void Show(int numA, int numB, Func<int, int, double> func)
{
//真正实现操作的方法由泛型参数传入
Console.WriteLine("利用泛型委托传入方法\n{0}{1}{2}={3}", numA, func(numA, numB).GetType().Name, numB, func(numA, numB));
Console.ReadLine();
}
static void Main(string[] args)
{
//比较之后一眼就能看出,两者之间唯一的差距就是参数传入的顺序问题!(夸张)
Show(1, 2, new Operation().Add);
Show(new Add(1, 2));
}
由此可见:
其实程序的本质就是算法+数据结构,而委托是一种由.net封装好的数据结构。委托定义好了它的内部操作,让方法能被当作参数传入其他函数,能让一个函数可以实现总体流程的复用,是让调用者“填空”(选择方法),自定义某个细节,让有类似操作的一系列方法进行复用。
从某种程度上来说,委托的思路其实就是一种设计模式,它的目的如上所述。