标签:存在 定义 pre 接下来 教程 href 说明符 rand isp
可以认为委托是持有一个或多个方法的对象。当然,正常情况下你不想“执行”一个对象,但委托与典型对象不同。可以执行委托,这时委托会执行它所“持有”的方法。
我们从下面的示例代码开始。具体细节将在本章剩余内容介绍。
如果你有C++背景,理解委托最快的方法是把它看成一个类型安全的、面向对象的C++函数指针
delegate void MyDel(int value);//声明委托类型 class Program { void PrintLow(int value) { Console.WriteLine("{0} - Low Value",value); } void PrintHigh(int value) { Console.WriteLine("{0} - High Value",value); } static void Main() { Program program=new Program(); MyDel del; //声明委托变量 var rand=new Random(); var randomValue=rand.Next(99); del=randomValue<50 ?new MyDel(program.PrintLow) :new MyDel(program.PrintHigh); del(randomValue); //执行委托 } }
委托和类一样,是用户自定义类型。但类表示的是数据和方法的集合,而委托持有一个或多个方法,以及一系列预定义操作。
可以通过以下操作步骤来使用委托。
你可以把delegate看做一个包含有序方法列表的对象,这些方法的签名和返回类型相同。
与类一样,委托类型必须在被用来创建变量以及类型的对象前声明。声明格式如下。
关键字 委托类型名 ↓ ↓ delegate void MyDel(int x); ↑ ↑ 返回类型 签名
虽然委托类型声明看上去和方法声明一样,但它不需要在类内部声明,因为它是类型声明。
委托是引用类型,因此有引用和对象。委托类型声明后,我们可以声明变量并创建类型的对象。
有两种创建委托对象的方法,一种是使用带new运算符的对象创建表达式,如下面代码所示。
delVar=new MyDel(myInstObj.MyM1); dVar=new MyDel(SClass.OtherM2);
我们还可以使用快捷语法,它仅由方法说明符构成。这种快捷语法能够工作是因为在方法名称和其相应的委托类型间存在隐式转换。
delVar=myInstObj.MyM1;
dVar=SClass.OtherM2;
创建委托对象会为委托分配内存,还会把第一个方法放入委托调用列表。
由于委托是引用类型,我们可以通过给它赋值来改变包含在委托变量中的引用。旧的委托对象会被GC回收。
MyDel delvar; delVar=myInstObj.MyM1; ... delVar=SClass.OtherM2;
迄今为止,我们见过的所有委托在调用列表中都只有一个方法。委托可以使用额外的运算符来“组合”。这个运算符会创建一个新的委托,其调用列表连接了作为操作数的两个委托的调用列表副本。
例:创建3个委托,第3个委托由前两个组合而成。
MyDel delA=myInstObj.MyM1; MyDel delB=SClass.OtherM2;
MyDel delC=delA+delB;
尽管术语组合委托(combining delegate)让我们觉得好想操作数委托被修改了,其实它们并没有被修改。事实上,委托是恒定的。委托对象被创建后不能再被改变。
尽管通过上一节我们知道委托是恒定的,不过C#提供了看上去可以为委托添加方法的语法,即使用+=运算符。
例:为委托的调用列表增加两个方法。
MyDel delVar=inst.MyM1; delVar+=SCL.m3; delvar+=X.Act;
当然,使用+=运算符时,实际发生的是创建了一个新的委托,其调用列表是左边的委托加上右边的组合。然后将这个新的委托赋值个delVar。
我们可以使用-=运算符从委托移除方法。
delVar-=SCL.m3;
与为委托增加方法一样,其实是创建了一个新的委托。新的委托是旧委托的副本–只是没有了已经被移除方法的引用。
移除委托时需要记住以下事项:
可以像调用方法一样简单地调用委托。调用委托的参数将会用于调用列表中的每个方法(除非有输出参数,我们稍后介绍)。
例:delVar委托接受一个整数值。使用参数调用委托会使用相同的参数值调用它调用列表中的每个成员
MyDel delVar=inst.MyM1; delVar+=SCL.m3; delVar+=X.Act; ... delVar(55);
如果一个方法在调用列表中出现多次,当委托被调用时,每次在列表中遇到该方法时它都会被调用一次。
如下代码定义并使用了没有参数和返回值的委托。有关代码的注意事项如下:
delegate void PrintFunction(); class Test { public void Print1() { Console.WriteLine("Print1 -- instance"); } public static void Print2() { Console.WriteLine("Print2 -- static"); } } class Program { static void Main() { var t=new Test(); PrintFunction pf; pf=t.Print1; pf+=Test.Print2; pf+=t.Print1; pf+=Test.Print2; if(null!=pf) { pf(); } else { Console.WriteLine("Delegate is empty"); } } }
如果委托有返回值并且调用列表中有一个以上方法,会发生下面的情况:
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() { var mc=new MyClass(); MyDel mDel=mc.Add2; mDel+=mc.Add3; mDel+=mc.Add2; Console.WriteLine("Value: {0}",mDel()); } }
如果委托有引用参数,参数值会根据调用列表中的一个或多个方法的返回值而改变。
在调用委托列表中的下一个方法时,参数的新值会传给下一个方法。
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() { var mc=new MyClass(); MyDel mDel=mc.Add2; mDel+=mc.Add3; mDel+=mc.Add2; int x=5; mDel(ref x); Console.WriteLine("Value: {0}",x); } }
匿名方法(anonymous method)实在初始化委托时内联(inline)声明的方法。
例:第一个声明了Add20方法,第二个使用匿名方法。
class Program { public static int Add20(int x) { return x+=20; } delegate int OtherDel(int InParam); static void Main() { OtherDel del=Add20; Console.WriteLine("{0}",del(5)); Console.WriteLine("{0}",del(6)); } } class Program { delegate int OtherDel(int InParam); static void Main() { OtherDel del=delegate(int x) { return x+20; }; Console.WriteLine("{0}",del(5)); Console.WriteLine("{0}",del(6)); } }
我们可以在如下地方使用匿名方法。
匿名方法表达式语法包含如下:
关键字 参数列表 语句块 ↓ ↓ ↓ delegate(Parameters){ImplementationCode}
在匿名方法的语法中,delegate关键字有点多余,因为编译器已经知道我们在将方法赋值给委托。我们可以很容易地通过如下步骤把匿名方法转换为Lambda表达式:
MyDel del=delegate(int x) {return x+1;};//匿名方法 MyDel le1= (int x) => {return x+1;};//Lambda表达式
术语Lambda表达式来源于数学家Alonzo Church等人在1920到1930年期间发明的Lambda积分。Lambda积分是用于表示函数的一套系统,它使用希腊字母Lambda(λ)来表示无名函数。近来,函数式编程语言(如Lisp及其方言)使用这个术语来表示可以直接用于描述函数定义的表达式,表达式不再需要有名字了。
除了这种简单的转换,通过编译器的自动推断,我们可以更进一步简化Lambda表达式。
MyDel del=delegate(int x) {return x+1;}; MyDel le1= (int x) => {return x+1;}; MyDel le2= (x) => {return x+1;}; MyDel le3= x => {return x+1;}; MyDel le4= x => x+1 ;
例:Lambda表达式完整示例
delegate double MyDel(int par); class Program { static void Main() { MyDel del=delegate(int x) {return x+1;}; MyDel le1= (int x) => {return x+1;}; MyDel le2= (x) => {return x+1;}; MyDel le3= x => {return x+1;}; MyDel le4= x => x+1 ; Console.WriteLine("{0}",del(12)); Console.WriteLine("{0}",le1(12)); Console.WriteLine("{0}",le2(12)); Console.WriteLine("{0}",le3(12)); Console.WriteLine("{0}",le4(12)); } }
有关Lambda表达式的参数列表的要点如下:
标签:存在 定义 pre 接下来 教程 href 说明符 rand isp
原文地址:http://www.cnblogs.com/moonache/p/6269113.html