配色: 字号:
委托_lambda_事件
2017-12-16 | 阅:  转:  |  分享 
  
委托_lambda_事件要点:委托Lambda表达式闭包事件弱事件引用方法委托相当于C++中的函数指针,C++中的函数指针只不过是一个指向内
存的位置的指针,它不是类型安全的。我们无法判断这个指针实际上指向什么,像参数和返回类型等项就更无从得知了。而.net委托完全不同,
委托是类型安全的,它定义了返回值类型和参数的类型。委托不仅包含对方法的引用,也可以包含对多个方法的引用。Lambda表达式与委托直
接相关。当参数是委托类型时,就可以使用lambda表达式实现委托引用的方法。委托当要把方法传递给其它方法时,需要用到委托。要了解它
们的含义,可以看看下面一行代码:IntI=int.Parse(“99”);我们习惯于皀数据作为参数传给方法,如上面的例子所示
。所以,给方法传递另一个方法听起来有点奇怪。而有时某个方法的执行的操作并不是针对数据进行的,而是要对另一个方法进行操作。更麻烦的是
在编译时我们不知道第二个方法是什么,这个信息只能在运行是得到,所需要把第二个方法传递给第一个方法。声明委托C#中使用一个类时,分两
个阶段。首先,需要定义这个类,即告诉编译器这个类由什么字段和方法组成。然后,实例化一个对象。使用委托时也需要经过这两个步骤。首先必
须定义要使用的委托,对于委托,定义就是告诉编译品这种类型的委托表示哪种类型的方法。然后,必须创建这个委托的一个或多个实例。编译器在
后台将创建表示该委托的一个类。定义委托的语法如下:delegatevoidIntMethodInvoker(intx);示例
中,定义了一个委托IntMethodInvoker,并指定该委托的每个实例都可以包含一个方法的引用,这个方法带有一个int类型的参
数,并返回void。理解委托的一个要点是它们的类型安全性非常高。在定义委托时,必须给出它所表示的方法签名和返回值类型等全部细节因为
定义委托基本上是定义一个新类,所以可以在定义类的任何地方定义委托(不能在方法内定义)。使用委托delegatestringG
etNameDelegete();staticvoidMain(string[]args){GetNameDelege
ted=GetName;Console.WriteLine(d());}staticstringGetName()
{return"getnamefunction";}当中d()等价于d.Invoke();标准的定法是d.Invok
e();为了少输入量,只需要委托的定例,就可以只传递地址的名称。这称为委托推断。只要编译器可以把委托实例解析为特定类型,这个C#特
性就是有效的.委托的一个特征是它们是类型安全的,可以确保被调用方法签名是正确的,但是它不关心在什么类型对象上调用这个方法,甚至不考
虑这个方法是静态方法,还是实例方法.简单的委托示例usingSystem;usingSystem.Collections.Ge
neric;usingSystem.Linq;usingSystem.Text;namespace简单的委托示例{clas
sMathOperations{publicstaticdoubleMultiplyByTwo(doubleval)
{returnval2;}publicstaticdoubleSquare(doubleval){re
turnvalval;}}}usingSystem;usingSystem.Collections.Generic
;usingSystem.Linq;usingSystem.Text;namespace简单的委托示例{classPro
gram{delegatedoubleDoubleOp(doubleval);staticvoidMain(str
ing[]args){DoubleOp[]operations={MathOperations.MultiplyBy
Two,MathOperations.Square};for(inti=0;i;i++){Console.WriteLine("Usingoperations[{0}]",i);ProcessAnD
ispalyNumber(operations[i],2.0);ProcessAnDispalyNumber(operation
s[i],7.94);ProcessAnDispalyNumber(operations[i],1.141);}}priv
atestaticvoidProcessAnDispalyNumber(DoubleOpdoubleOp,double
v){Console.WriteLine(doubleOp(v));}}}Action和Func委托除了为每个
参数和返回类型定义一个新委托类型外,还可以使用Action和Func委托。泛型Action委托表示引用一个voi
d返回类型的方法。这个委托存在不同的变体,可以传递至多16个不同的参数类型。Action类可以调用没有参数的方法。Func
使用方法类似,允许调用带返回类型的的方法。BubbleSorter示例usingSystem;usingSystem.Coll
ections.Generic;usingSystem.Linq;usingSystem.Text;namespaceBub
bleSorter{publicclassSorter{staticpublicvoidSort(IList
sorArray,Funccomparison){boolswapped=true;do
{swapped=false;for(inti=0;i{if(comparison(sorArray[i+1],sorArray[i])){Ttemp=sorArray[i
];sorArray[i]=sorArray[i+1];sorArray[i+1]=temp;swapped
=true;}}}while(swapped);}}}usingSystem;usingSystem.Col
lections.Generic;usingSystem.Linq;usingSystem.Text;namespaceBu
bbleSorter{classProgram{staticvoidMain(string[]args){int
[]a={3,6,9,8,5,2,1,4,7};Sorter.Sort(a,Compare);foreach(va
ritemina){Console.Write(item);Console.Write("");}Console
.WriteLine();}staticboolCompare(intx,inty){returnx}}}多播委托委托也可以包含多个方法,这种委托叫做多播委托。这种多播委托可以按顺序调用多个方法。为此委托的签名就必须必成void
返回;否则就只能得到委托调用的最后一个方法的结果。delegatevoidDoubleOp(doubleval);stat
icvoidMain(string[]args){DoubleOpoperations=MathOperation
s.MultiplyByTwo;operations+=MathOperations.Square;operations
(5);}classMathOperations{publicstaticvoidMultiplyByTwo(dou
bleval){Console.WriteLine(val2);}publicstaticvoidSquare(
doubleval){Console.WriteLine(valval);}}如果正在使用多播委托,就应知道对同一
个委托调用方法链的顺序并未正式定义,因此应避免编写依赖以特定顺序调用方法的代码。通过一个委托调用多个方法还有可能导致一个大问题。多
播委托含有逐个调用委托的集合。如果当中有一个方法抛出异常,整个迭代就会停止。在这种情况下,为了避免这个问题,应该自己迭代方法列表。
Delegate类定义GetInvocationList()方法,它返回一个Delegate对象数组。现在可以使用这个委托调用与委
托相关的方法,捕获异常,并继续下一次迭代。DoubleOpoperations=MathOperations.Multip
lyByTwo;operations+=MathOperations.Square;Delegate[]delegat
es=operations.GetInvocationList();foreach(DoubleOpiteminde
legates){item(6);}匿名方法Funcd=delegate(inta){
Console.WriteLine("indelegate");returna.ToString();};Consol
e.WriteLine(d(45));在使用匿名方法时,必须遵循两条规则。在匿名方法不能使用跳转语句(break,gotoco
ntinue),跳到匿名方法的外部,反之亦然;在匿名方法中不能访问不安全的代码。另外,也不能访问在匿名方法外部使用的ref和out
参数。但可以使用匿名方法外部定义的其它变量。从C#3.0开始,可以使用lambda代替匿名方法Lambda表达式staticvo
idMain(string[]args){Funclambda=a=>{Consol
e.WriteLine("indelegate");returna.ToString();};Console.Write
Line(lambda(45));}=>左边列出了需要的参数,右边定义的方法体。闭包Lambda可以访问表达式外部的变量。这称为
闭包。它是一个非常好的功能,但如果使用不当,会非常危险。如何在lambda访问外部变量?为了理解这一点,看看编译器在定义lambd
a表达式时做了什么。对于lambda表达式x=>x+someVal.编译器会创建一个匿名类,它有一个构造函数来传递外部变量。如下面
的代码示:publicclassAnonymousClass{privateintsomeVal;publicAn
onymousClass(intsomeVal){this.someVal=someVal;}publicint
AnonymousClassMethod(intx){returnx+someVal;}}事件事件基于委托,为
委委托提供了一种发布/记阅机制。在架构内到处都能看到事件。在Windows应用程序中,Button提供Click事件。这类事件就是
委托。触发Click事件时调用的处理方法需要定义,其参数同委托类型定义、。在本节的示例代码中,事件用于连接CarDealer类和C
onsumer类。CarDealer类提供了一个新车到达时的触发事件。Consumer类订阅该事件,以获得新车到达的通知。事件发存
程序usingSystem;usingSystem.Collections.Generic;usingSystem.Linq
;usingSystem.Text;namespace事件{publicclassCarInfoEventArgs:
EventArgs{stringcar;publicCarInfoEventArgs(stringcar){thi
s.car=car;}publicstringCar{get{returncar;}set{car
=value;}}}}usingSystem;usingSystem.Collections.Generic;usi
ngSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;nam
espace事件{publicclassCarDealer{publiceventEventHandlerInfoEventArgs>NewCarInfo;publicvoidNewCar(stringcar){Conso
le.WriteLine(car);RaiseNewCarInfo(car);}privatevoidRaiseNewC
arInfo(stringcar){EventHandlernewCarInfo=
NewCarInfo;if(newCarInfo!=null){newCarInfo(this,newCarInfo
EventArgs(car));}}}}事件侦听器usingSystem;usingSystem.Collections
.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threadi
ng.Tasks;namespace事件{classConsumer{stringname;publicstrin
gName{get{returnname;}set{name=value;}}publicCons
umer(stringname){this.name=name;}publicvoidNewCarIsHere(
objectsender,CarInfoEventArgse){Console.WriteLine("{0}:Car{1}
",name,e.Car);}}}连接事件发布器和订阅器vardealer=newCarDealer();var
michael=newConsumer("Micael");dealer.NewCarInfo+=michael.N
ewCarIsHere;dealer.NewCar("Ferrari");dealer.NewCarInfo-=micha
el.NewCarIsHere;dealer.NewCar("Ferrari_2");弱事件通过事件,直接连接到程序和侦听器。但
是垃圾回收有一个问题。例如,如果侦听器不再引用,发布程序仍有一个引用。垃圾回收器不能清空侦听器所占用的内存,因为发布程序仍保有一个
引用,会针对侦听事件。这种强连接可以通过弱事件模式来解决,即使用用WeakEventManger作为发布程序和侦听器之间的中介。前
面的示例把CarDealer作为发布程序,把Consumer作为侦听器,下一节将修改这个示例,以使用弱事件模式。vardealer=newCarDealer();varmichael=newConsumer("Micael");WeakEventManager.AddHandler(dealer,"NewCarInfo",michael.NewCarIsHere);dealer.NewCar("Mercedes");dealer.NewCar("Mercedes1");WeakEventManager.RemoveHandler(dealer,"NewCarInfo",michael.NewCarIsHere);dealer.NewCar("Mercedes2");WeakEventManager在WindowsBase.dll中使用前需引用
献花(0)
+1
(本文系luan_it首藏)