回调(callback)函数是Windows编程的一个重要部分。如果具备C或C++编程北京,应该就曾在许多Windows API中使用过回调。Visual Basic 添加了AddressOf关键字后,开发人员就可以利用以前一度受到限制的API了。回调函数实际上是方法调用的指针,也称为函数指针,是一个非常强大的编程特性。.net已委托的形式实现了函数指针的概念。他们的特殊之处是,与C函数指针不同,.net委托是类型安全的。这说明,C中的函数指针只不过是一个指向存储单元的指针我们无法说出这个指针实际指向什么,像参数和返回类型等就更无从知晓了。
委托是一种特殊的对象类型,其特殊之处在于,我们以前定义的所有对象都包含数据,而委托包含的只是方法的地址。理解委托的一个要点是他们的类型安全性非常高。在定义委托时,必须给出它所代表的方法签名和返回类型等全部细节。因为定义委托基本上是定义一个新类,所以可以在定义类的任何地方定义委托,既可以在另一个类的内部定义,也可以在任何类的外部定义,还可以在命名空间中把委托定义为顶层对象。根据定义可见性,可以在委托定义上添加一般的访问修饰符:public,private,protected等。
在c# 中使用委托
private delegaete string GetAString(); static void Main() { int x=40; GetAString firstStringMethod =new GetAString(x.ToString); Console.WriteLine("String is {0}"+firstStringMethod()); }
这段代码中,实例化了类型为GetAString的一个委托,并对他进行初始化,是他引用整型变量X的ToString()方法。实际上,给委托实例类型提供括号与调用委托类型的Invoke()方法完全相同。firstStringMethod是委托类型的一个变量,所以C#编译器会用fristStringMethod.Invoke()代替firstStringMethod()。
C#2.0 使用委托推断扩展了委托的语法。为了减少输入量,只需要委托实例,就可以只传送地址的名称。这称为委托推断。
GetAString firstStringMethod =new GetString(x.ToString);
只要用变量x把方法名传送为变量firstStringMethod,就可以编写出作用相同的代码:
GetString fristStringMethod=x.ToString;
给定委托的实例可以表示任何类型的任何对象上的实例方法或静态方法,只要方法的签名匹配于委托的签名即可。
多播委托
上述使用的委托只包含一个方法调用。调用委托的次数与调用方法的次数相同。如果要调用多个方法,就需要多次显示调用这个委托。委托也可以包含多个方法。这种委托称为多播委托。多播委托的签名通常放回void,若不是,则返回委托最后调用方法的返回值。如果使用多播委托,就应该注意对同一个委托调用方法链的顺序并未正式定义,因此应避免编写依赖于已特定顺序调用方法的代码。
DoubleOp operrations=MathOperations.MultiplyByTwo;
operrations+=MathOperations.Square;
多播委托包含一个逐个调用的委托集合。如果通过委托调用的一个方法抛出了异常,整个迭代就会停止。为了避免这个问题,可以手动迭代委托列表。Delegate类定了方法GetInvocationList(),它返回一个Delegage 对象数组。
static void Main() { DemoDelegate dl=One; dl+=Two; Delegate[] delegates=dl.GetInvocationLsit(); foreach(Delegate d in delegates) { try { d(); } catch(Exception ex) { } } }
匿名方法
使用委托的另一种方式:即通过匿名方法。匿名方法是用作委托参数的一个代码块。
1 using System; 2 namespace Wrox.ProCsharp.Delegates 3 { 4 class Program 5 { 6 delegate string DelegateTest(string val); 7 static void Main() 8 { 9 string mid =",middle part,"; 10 delegateTest anonDel=delegate(string param) 11 { 12 param+=mid; 13 param+=" and this was added to the string."; 14 return param; 15 }; 16 Console.WriteLine(anonDel("Strt of String")); 17 } 18 } 19 }
可以看出,该代码块使用方法级的字符串变量mid, 该变量是在匿名方法的外部定义的 ,并添加到要传送的参数中 。接着代码返回该字符串值 。在调用委托时,把一个字符串传送为参数 ,将返回的字符串输出到控制台 上 使用匿名方法时,代码执行的不太快。编译器任然定义了一个方法,该方法有一个自动指定的名称;
在使用匿名方法时必须遵循两个规则。在匿名方法中不能使用跳转语句调到匿名方法的外部,反之亦然,匿名方法外部的跳转语句也不能跳转到该匿名方法的内部。在匿名方法的内部也不能访问不安全的代码。另外也不能访问在匿名方法外部使用的ref和out参数。