1.什么是委托
==理解委托最快的方式是把它看成一个类型安全的,面向对象的c++h函数指针!==
2.委托的概述
可以通过以下操作来使用委托:
1. 声明一个委托类型,委托声明看上去和方法声明类似,只是没有实现块。
2. 使用该委托类型声明一个委托变量。
3. 创建委托类型的对象,把它赋值给委托变量。新的委托对象包括指向某个方法的引用,这个方法和第一步定义的签名和返回类型一致。
4. 你可以选择为委托对象增加其他方法,这些方法必须与第一步中定义的委托类型有相同的签名和返回类型。
5. 在代码中你可以像调用方法一样调用委托,在调用委托的时候,其包含的每一个方法都会被执行。
委托和类一样,是一种用户自定义类型,但类表示的是数据和方法的集合,而委托则持有一个或多个方法,以及一系列预定义操作。
可以把delegate看作一个包含有序方法列表的对象,这些方法具有相同的签名和返回类型。
- 方法的列表成为调用列表
- 委托保存的方法可以来自任何类和结构,只要它们在下面两点匹配:
- - 委托的返回类型。
- - 委托的签名(包括ref和out修饰符)。
- 调用方法中的方法可以是实例方法也可以是静态方法。
- 在调用委托的时候,会执行其调用列表中的所有方法。
delegate void MyDel (int x);
- delegate:关键字
- void:返回类型
- MyDel:委托类型名
- int x:签名
delegate void MyDel(int x);//声明委托类型
MyDel delVar,dVar;//创建两个委托变量
delVar=new MyDel(myInstObj.MyM1);//创建委托并保存引用
dVar=new MyDel(SClass.OtherM2);
除了为委托分配内存,创建委托对象还会把第一个方法放入委托的调用列表。
也可以使用快捷语法
MyDel delVar=myInstObj.MyM1;
3.组合委托
委托可以使用额外的运算符来“组合”。这个运算最终会创建一个新的委托,其调用列表连接了作为操作数的两个委托的调用列表副本。
例如,如下代码创建了3个委托,第3个委托由前两个委托组合而成
MyDel delA=myInstObj.MyM1;
MyDel delB=SClass.Other2;
MyDel delC=delA+delB;
委托并没有被修改,委托是恒定的,委托对象被创建后不能再被改变。
4.为委托添加和移除方法
MyDel delVar=inst.MyM1;//创建并初始化
delVar+=SCl.m3;//增加方法
delVar+=X.Act;
delVar-=SCl.m3;//从委托移除方法
在使用+=运算符时,实际发生的是创建了一个新的委托,其调用列表是左边的委托加上右边方法的组合。然后将这个新的委托赋值给delVar。而移除也是创建了一个新的委托,新的委托是旧的副本——只是没有了被移除方法的引用。
以下是移除委托时需要记住的一些事项:
- 如果在调用列表中的方法有多个实例,-=运算符将从列表最后开始搜索,并且移除第一个与方法匹配的实例。
- 试图删除委托中不存在的方法没有效果
- 试图调用空委托会抛出异常。我们可以通过把委托和Null进行比较来判断委托的调用列表是否为空,如果空,则委托是null。
5.委托的实例
delegate void PrintFunction();
class Test
{
public void Print1(){
Console.WriteLine("111");
}
public static void Print2(){
Console.WriteLine("222");
}
class Program{
static void main(){
Test t=new Test();
PrintFunction pf;
pf=t.Print1();
//给委托增加三个另外的方法
pf+=Test.Print2;
pf+=t.Print1();
pf+=Test.Print2();
if(pf!=null)
pf();
else
Console.WriteLine("444");
}
}
}
输出:111
222
111
222
6.调用带返回值的委托
如果委托有返回值并且在调用列表中有一个以上的方法,会发生下面的情况:
- 调用列表中最后一个方法返回的值就是委托调用返回的值。
- 调用列表中所有其他方法的返回值都会被忽略。
delegate int MyDel();
class MyClass{
int IntValue=5;
public int Add2(){IntValue+=2;return IntValue;}
public int Add3(){IntValue+=3;return IntValue;}
}
class Program{
static void Main(){
MyClass mc=new MyClass();
MyDel mDel=mc.Add2;
mDel+=mc.Add3;
mDel+=mc.Add2;
Console.WriteLine("Value:{0}",mDel());//调用委托并使用返回值
}
}
输出:Value:12
7.调用带引用参数的委托
如果委托有引用参数,参数值会根据调用列表中的一个或多个方法的返回值而改变。
- 在调用委托列表中的下一个方法时,参数的新值会传给下一个方法。
delegate void MyDel(ref int x);
class MyClass{
public int Add2(ref int x){x+=2;}
public int Add3(ref int x){x+=3;}
static void Main(){
MyClass mc=new MyClass();
MyDel mDel=mc.Add2;
mDel+=mc.Add3;
mDel+=mc.Add2;
int x=5;
mDel(ref x);
Console.WriteLine("Value:{0}",x);//调用委托并使用返回值
}
}
输出:Value:12
8.匿名方法
==匿名方法是在初始化委托时内联声明的方法==
我们可以在如下地方使用匿名方法:
- 声明委托变量时作为初始化表达式。
- 组合委托时在赋值语句的右边。
- 为委托增加事件时在赋值语句的右边。
- 外围作用域的变量叫做外部变量。
- 用在匿名方法实现代码中的外部变量成为被方法捕获。
匿名方法的语法
- 返回类型
delegate int OtherDel(int InParam);
OtherDel del =delegate(int x){
return x+20;//返回一个Int值
};//记得分号
- 参数
参数列表可以为空,条件如下:
- 委托的参数列表不包含任何out参数。
- 匿名方法不使用任何参数。
- params参数
- 委托类型声明指定最后一个参数为params类型的参数。
- 然而,匿名方法参数列表忽略params关键字。(省略)
9.Lambda表达式
C#2.0引入了匿名方法,C#3.0引入了Lambda表达式,简化匿名方法的语法
我们可以通过以下步骤把你那个方法转换成Lambda表达式
- 删除delegate关键字
- 在参数列表和匿名方法主体之间放Lambda运算符=>读作goes to
- Lambda表达式允许我们省略类型参数,如le2
- 如果只有一个隐式类型参数,我们可以省略圆括号,如le3
- - 带有类型的参数列表称为显式类型
- - 省略类型的参数列表称为隐式类型
- 可以将语句块替换为return关键字后的表达式,如le4
- Lambda表达式参数列表中的参数必须在参数数量,类型和位置上与委托相匹配
- 如果没有参数,必须使用空括号
MyDel del=delegate(int x){return x+1;};//匿名方法
MyDel le1=(int x)=>{return x+1;};//Lambda表达式
MyDel le2=(x)=>{return x+1;};//Lambda表达式
MyDel le3= x=>{return x+1;};//Lambda表达式
MyDel le4=x=>x+1;//Lambda表达式