C# 委托
C#图解教程,第四版,第十三章,委托,学习笔记
可以认为委托是持有一个或多个方法的对象。
但委托与典型的对象不同,可以执行委托,这时,委托会执行它持有的方法。
委托和类一样,是一种用户自定义的类型。
但类表示的是数据和方法的集合,而委托则持有一个或者多个方法,以及一系列预定义操作。
可以通过以下操作步骤来使用委托,
①声明一个委托类型。委托声明看上去和方法类似,只是没有实现块
delegate void MyDel(int value);
②使用该委托类型,声明一个委托变量。
MyDel del;
③创建委托类型的对象,把它赋值给变量,新的委托对象包括指向某个方法的引用,这个方法与第一步定义的委托类型的签名和返回类型一致。
del = new MyDel( 类.方法 );
④你可以选择为委托对象增加其他方法,这些方法必须与第一步定义的委托类型的签名和返回类型一致。
⑤像调用方法一样调用委托。调用委托时,其包含的每一个方法都会被执行。
del(int类型变量);
你可以把 delegate 看作一个包含有序方法列表的对象,这些方法具有相同的签名和返回类型。
方法的列表称为调用列表。
委托保存的方法可以来自任何类或者结构,只要他的返回类型和签名包括ref与out与委托类型一致。
在调用列表中的方法可以是实例方法也可以是静态方法。
调用委托时,会执行其调用列表中所有方法。
声明委托类型
delegate void MyDel(int x)
关键字 返回类型 委托类型名 签名
返回类型和签名指定了委托接受的方法的形式。
委托类型声明看上去与方法的声明很相似,但有两个方面不同。
委托类型声明以delegate关键字开头,且没有方法主体。
创建委托对象
MyDel delvar
委托类型 变量
有两种创建委托对象的方式
第一种是使用 new 运算符的对象创建表达式。
delvar = new MyDel(类.方法)
第二种是
delvar = 类.方法
这种快捷语法是因为方法名称与其相对的委托类型之间存在隐式转化
给委托赋值
委托是引用类型,我们可以通过给它赋值来改变包含在委托变量中的引用。旧的对象会被垃圾回收器回收。
delvar = 类1.方法;
delvar = 类2.方法;
组合委托
委托可以使用额外的运算符来“组合”。这个运算最终会创建一个新的委托,其调用列表连接了作为操作数的两个委托的调用列表副本。
MyDel delA = 类.方法;
MyDel delB = 类.方法;
MyDel delA = delA+delC;
尽管组合委托让我们觉得好像操作数委托被修改了,其实他们并没有被修改,事实上,委托是恒定的,委托对象创建之后不能再被改变。
C3提供了看上去可以为委托添加方法的语法,即+=运算符。
MyDel delvar = 类.方法;
delvar += 类.方法;
delvar += 类.方法;
当然在使用+=运算符时,实际发生的是创建了一个新的委托。
从委托中移除方法
我们还可以使用-=来移除委托,但有几点需要注意。
①如果在调用列表中的方法有多个实例,-=会从最后开始搜索,并且移除第一个与方法匹配的实例。
②试图删除委托中不存在的方法将没有效果。
③试图调用空委托将会发生异常,可以使用 null 来判断委托是否为空。
调用委托
MyDel delvar = 类.方法;
delvar += 类.方法;
delvar += 类.方法;
…
delvar(传入方法的值);
调用带返回值的委托
如果委托有返回值并且在调用列表中有一个以上的方法,会发生先的情况:
①调用列表的最后一个方法返回的值就是委托调用返回的值。
②调用列表中所有其他方法返回的值都会被忽略。
调用带引用参数的委托
如果委托有引用参数,参数值会根据调用列表中的一个或多个方法的返回值儿改变。
在调用委托列表中的下一个方法时,参数的新值(不是初始值)会传给下一个方法。
匿名方法
匿名方法允许我们避免使用独立的具名方法。
匿名方法是在初始化委托时内连声明的方法。
即在开头步骤中的第三步不需要调用某个类中的方法。
MyDel delvar = delegate(int x){return x+20;};
返回数据的类型必须同委托类型中的相同。
除了数组参数,匿名方法的参数列表必须在如下三个方面与委托匹配:
①参数数量②参数类型及位置③修饰符。
可以通过省略园括号或使圆括号为空来简化匿名方法的参数列表,但必须满足两个条件:
①委托参数列表不包含out参数②匿名方法不使用任何参数。
Lambda 表达式
C#2.0引入了匿名方法,C#3.0引入了Lambda表达式,简化了匿名语法的语法。
在匿名方法的语法中delegate关键字是有点多余,因为编译器已经知道我们在将方法赋值给委托,
我们可以通过以下步骤将匿名方法转换成Lambda表达式:
①删除 delegate 关键字
②在参数列表和匿名方法中添加Lambda运算符 => ,Lambda运算符读作 goes to。
MyDel del = delegate(int x) {return x+1;};
MyDel del = (int x) => {return x+1;};
但这样只省去了6个字符,然而编译器可以通过推断,允许我们进一步简化。
编译器可以从委托声明中知道委托参数的类型,因此Lambda表达式允许我们省略类型参数:
MyDel del = ( x) => {return x+1;};
如果只有一个隐式类型参数(省略类型的参数列表)我们可以省略周围的圆括号。
MyDel del = x => {return x+1;};
最后,Lambda 表达式允许表达式的主体是语句块或表达式。
如果语句包含了一个返回语句,我们可以将语句块替换为 return 关键字后的表达式。
MyDel del = x => x+1 ;
有关Lambda表达式的参数列表的要点如下:
①Lambda表达式的参数列表中的参数必须在参数数量,类型和位置上与委托相匹配。
②表达式的参数列表中的参数不一定需要包含类型,除非委托有ref或out参数,此时必须注明类型。
③如果只有一个参数,并且是隐式类型的,那么圆括号可以省略,否则必须有括号。
④如果没有参数必须使用一组空的圆括号。
关于委托有什么用的讨论:http://bbs.csdn.net/topics/390751609/
关于委托和事件的妙用:http://blog.csdn.net/susan19890313/article/details/6949738