一、委托
委托在某种程度上提供了简介的方法。换言之,不需要直接制定一个要执行的行为,而是将这个行为用某种方式“包含”在一个对象中。这个对象可以像其他任何对象那样使用。举个例子,遗嘱由一系列指令组成。而遗嘱一般是某人趋势之前写好的,然后把它放在一个安全的的地方。去世后,律师按照指示去执行。
C#中的委托和遗嘱一样,也要在恰当的时间执行一系列的操作。如果代码想要执行操作,但不知道操作细节,一般使用委托。
1.1 简单委托的完成
为了让委托做事,必须满足4个条件:
- 声明委托类型;
- 必须有一个方法包含了要执行的代码;
- 必须创建一个委托实例;
- 必须调用(invoke)委托实例。
1.1.1 声明委托类型
委托类型实际上只是参数类型的一个列表以及一个返回类型。它规定了类型的实例能表示的操作。例如:
delegate void StringProcessor(string input);
上述代码指出,如果要创建StringProcessor的一个实例,需要只带一个参数的方法,而且之歌方法要有一个void返回类型。
1.1.2 为委托实例的操作找到一个恰当的方法
以下StringProcessor实例的5个备选方法:
void PringString (string x)
void PringInteger(int)
void PrintTwoStrings(string x,string y)
int GetStringLength(string x)
void PrintObject(object x)
很明显,1符合要求,2、3、4不符合要求。对于5来说,string是由object派生而来的,把这个方法作为StringProcessor的一个实例来使用是合情合理的,但C#1要求委托必须具有完全相同的参数类型。C#2改善了这个状况。
1.1.3 创建委托实例
具体用什么形式的表达式来创建委托实例,取决于操作使用实例方法还是静态方法。如果操作的是静态方法,指定类型名称就可以了,如果操作的是实例方法,就需要先创建类型的一个实例。
StringProcessor proc1,proc2;
proc1 = new StringProcessor(StaticMethods.PrintString);
InstanceMethods instance = new InstanceMethods();
proc2 = new StringProcessor(instance.PrintString);
如果操作的是实例方法,并试图从静态方法中创建一个委托实例,就仍需要提供提供一个引用作为目标。所谓“提供一份引用作为目标”是指仍然要写成
proc2 = new StringProcessor(instance.PrintString);这样的形式,而不能写成proc2 = new StringProcessor(PrintString);。
1.1.4 调用委托实例
调用一个委托实例的方法就行了。这个方法本身被称为Invoke。在委托类型中,这个方法以委托类型的形式出现,并且具有委托类型声明中指定的相同参数列表和返回类型。所以有一个这样的方法 void Invoke(string input)
调用Invoke会执行委托实例的操作,向它传递在调用Invoke时指定的任何参数。另外,如果返回类型不是void,还要返回操作的返回值。
op1=>operation: proc1("Hello")
op2=>operation: 编译成proc1.Invoke("Hello")
op3=>operation: 在执行时调用PrintString("Hello")
op1->op2->op3
1.1.5 一个完整的例子
各种简单的方式使用委托
using System;
delegate void StringProcessor(string input);//1.声明委托类型
class Person
{
string name;
public Person(string name){this.name = name;}
public void Say(string message)//2.声明兼容的实例方法
{
Console.WriteLine.{"{0} says: {1}",name,message};
}
}
class Background
{
public static void Note(string note)//3.声明兼容的静态方法
{
Console.WriteLine("({0})",note);
}
}
class SimpleDelegateuse
{
static void Main()
{
Person jon = new Person("Jon");
Person tom = new Person("Tom");
StringProcessor jonsVoice,tomsVoice,background;
jonsVoice = new StringProcessor(jon.Say);
tomsVoice = new StringProcessor(tom.Say);
background = new
StringProcessor(Background.Note);//4.创建3个委托实例
jonsVoice("Hello,son");
tomsVoice("Hello,Daddy!");
background("An airplane flies past.");//5.调用委托实例
}
}
委托相当于指定一些代码在特定的时间执行,那时,你也许已经无法封盖要执行的代码。如果希望在单击一个按钮后发生某事,但不想对按钮的代码进行修改,只是希望按钮调用某个方法,方法能执行恰当的操作。委托的是指是简介完成某种操作,事实上,许多面向对象编程技术都在做同样的事。
1.2 合并和删除委托
真是情况下,委托实例并不是只有一个操作,而是稍微复杂一点:委托实例实际有一个操作列表与之关联,这称为委托实例的调用列表。System.Delegate类型的静态方法Combine和Remove负责创建新的委托实例。其中,Combine负责将两个委托实例的调用列表连接起来,而Remove负责从一个委托实例中删除另一个实例的调用列表。
委托是不易变的,创建委托实例后,有关它的一切就不能改变。
很少在C#代码中看到对Delegate.Combine的显式调用,一般都是使用+和+=操作符。如图所示,其中x和y都是相同的委托类型变量。
op1=>operation: x += y;
op2=>operation: x = x + y;
op3=>operation: x = Delegate.Combine(x,y);
op1->op2->op3
除此之外,C#使用Delegate.Remove方法从一个实例中删除另一个实例的调用。C#使用-和-=运算符。Delegate.Remove(source,value)将创建的一个新的委托实例,其调用列表来自source,value中的列表被删除。如果结果有一个空的调用列表,则返回null。
调用委托实例时,它的所有操作都顺序执行。如果委托的签名具有一个非void的返回类型,则Invoke的返回值是最后一个操作的返回值。很少有非void的委托实例在它的调用列表中指定多个操作,因为这意味着其他所有操作的返回值永远看不见。除非每次调用代码使用Delegate.GetInvocationList获取操作列表时,都显式调用某个委托。
1.3 委托总结
- 委托封装了包含特殊返回啦欸行和一组参数的行为,类似包含单一方法的接口;
- 委托类型声明中所描述的类型签名决定了哪个方法可用于创建委托实例,同时决定了调用的签名;
- 为了创建委托实例,需要一个方法以及(对于实例方法来说)调用方法的目标;
- 委托实例是不易变的;
- 每个委托实例都包含一个调用列表——一个操作列表;
- 委托实力可以合并到一起,也可以从一个委托实例删除另一个;
- 事件不是委托实例——只是成对的add/remove方法。