学习内容:                                                                  

  • 委托是一中类型安全的对象,指向可以以后调用的其他方法。和传统的C++函数指针不同,.NET委托是内置支持多路广播和异步方法调用的对象。
  • 学会如何创建与应用委托类型之后,接下来研究event关键字,它使我们处理委托类型的过程更加简化和高效.
  • 然后,讨论C#中与委托和事件相关的语言特性,包括匿名方法和方法组转换.
  • 最后,会讨论将Lambda表达式.其实Lambda表达式只是匿名方法的一种伪装.

.NET委托类型                                                                                                                                                                                   

  历史上,Windows API经常使用C语言风格的函数指针来创建成为回调函数的实体.使用回调,程序员可以使一个函数返回报告给(即回调)程序中的另一个函数.

  委托类型包含3个重要的信息:

  • 它所调用的方法的名称
  • 该方法的参数(可选)
  • 该方法的返回值(可选)

  当一个委托被创建并提供了上述信息后,它可以在运行时动态调用其指向的方法.可以看到,.net框架中每个委托都被自动赋予同步或异步访问方法的能力,可以不用手工创建和管理一个Thread对象而直接调用另一个辅助执行线程上的方法,这大大简化了编程工作.

  实际上,委托定义在使用它的类型作用域里是很普遍的.还要注意编译器会将委托转换为一个完整的类定义,我们实际上做的是在类中创建n个嵌套类(委托).最重要的一点是:委托是一个类.理解了这个概念将有利于将来的学习.

  最简单的委托实例:

 

1 public delegate int BinaryOp(int x, int y);
2 class SimpleMath
3 {
4 public static int Add(int x, int y)
5 { return x + y; }
6 public static int Subtract(int x, int y)
7 { return x - y; }
8 }
9 class program
10 {
11 static void Main(string[] args)
12 {
13 Console.WriteLine("******Simple Delegate Example*******");
14 BinaryOp b = new BinaryOp(SimpleMath.Add);
15 Console.WriteLine("10+10 is {0}", b(10, 10));
16 Console.ReadLine();
17 }
18 }

 

  上面的实例中,要注意的是BinaryOp委托的格式,它指向任何一个带有两个整形参数并返回一个整数的方法.因此,我们可以创建一个名为SimpleMath类,其中定义两个完全匹配BinaryOp定义模式的静态方法.

  实例中在定义了BinaryOp委托的同时,自动做了一系列的步骤:生成的BinaryOp类自动定义了3个公共方法.Invoke()可能是核心方法,因为它被用来以同步方式调用委托类型维护的每个方法.其次是BeginInvoke()和EndInvoke()方法.能在第二个执行线程上异步调用当前方法.如果读者有多线程背景知识,就会明白开发人员创建第二个执行线程的一个最常见原因就是调用比较耗时的方法.委托顺带提供了多线程的功能.

 

public delegate int BinaryOp(int x, int y);
//自动产生的类分解如下:
sealed class BinaryOp:System.MulticastDeltegate
{
public BinaryOp(object target,uint functionAddress);
public int Invoke(int x,int y);
public IAsyncResult BeginInvoke(int x,int y,AsyncCallback cb,object state);
public int EndInvoke(IAsyncResult result);
}

 

  .NET是类型安全的,所以如果读者试图将一个不匹配的方法传入委托,将会收到编译错误.

.NET委托实例                                                            

  以上的实例纯粹用来说明委托作用的,因为仅为加两个数创建一个委托没有多大必要,为了提供更现实委托应用.我们来看下面的Car实例.使用.NET委托而不是自定义回调接口来发送通知.下面是我们要进行的步骤:

  • 定义将通知发送给调用者的委托类型.
  • 声明Car类中每个委托类型的成员变量
  • 在Car上创建辅助函数使调用者能指定由委托成员变量保存的方法
  • 修改Accelerate方法以在适当的情况下调用委托的调用列表

 

代码

 

代码
static void Main(string[] args)
{
Console.WriteLine(
"***** Delegates as event enablers *****\n");

Car c1
= new Car("SlugBug", 100, 10);

Car.Exploded d
= new Car.Exploded(CarExploded);
c1.OnAboutToBlow(
new Car.AboutToBlow(CarIsAlmostDoomed));
c1.OnAboutToBlow(
new Car.AboutToBlow(CarAboutToBlow));
c1.OnExploded(d);

Console.WriteLine(
"***** Speeding up *****");
for (int i = 0; i < 6; i++)
c1.Accelerate(
20);

c1.RemoveExploded(d);

Console.ReadLine();
}

public static void CarAboutToBlow(string msg)
{ Console.WriteLine(msg); }

public static void CarIsAlmostDoomed(string msg)
{ Console.WriteLine(
"Critical Message from Car: {0}", msg); }

public static void CarExploded(string msg)
{ Console.WriteLine(msg); }
}

 

更复杂的委托示例                                                          
  为了说明更高级的委托使用方法,首先创建一个CarGarage的控制台应用程序项目,让我们修改Car类是指包含两个新的Bool变量.一个用来决定是否应该洗车(isDirty);另一个表示该汽车是否需要互换轮胎(shouldRotate).
  
private bool isDirty;
private bool shouldRotate;
public Car(string name, int max, int curr, bool rotateTires, bool washCar)
{
currSpeed
= curr;
maxSpeed
= max;
petName
= name;
isDirty
= washCar;
shouldRotate
= rotateTires;
}

 

public bool Dirty
{
get { return isDirty; }
set { isDirty = value; }
}

public bool Rotate
{
get { return shouldRotate; }
set { shouldRotate = value; }
}//以上代码添加到Car

 

代码
public class Garage
{
private List<Car> theCars = new List<Car>();

public Garage()
{
theCars.Add(
new Car("Viper", 100, 0, true, false));
theCars.Add(
new Car("Fred", 100, 0, false, false));
theCars.Add(
new Car("BillyBob", 100, 0, false, true));
}

// 把CarDelegate作为一个参数
public void ProcessCars(Car.CarMaintenanceDelegate proc)
{
foreach (Delegate d in proc.GetInvocationList())
{
Console.WriteLine(
"***** Calling: {0} *****",
d.Method);
}

if (proc.Target != null)
Console.WriteLine(
"\n-->Target: {0}", proc.Target);
else
Console.WriteLine(
"\n-->Target is a static method");

foreach (Car c in theCars)
{
Console.WriteLine(
"\n-> Processing a Car");
proc(c);
}
Console.WriteLine();
}
}

 

代码
public class ServiceDepartment
{
// A target for the delegate.
public void WashCar(Car c)
{
if (c.Dirty)
Console.WriteLine(
"Cleaning a car");
else
Console.WriteLine(
"This car is already clean...");
}

// Another target for the delgate.
public void RotateTires(Car c)
{
if (c.Rotate)
Console.WriteLine(
"Tires have been rotated");
else
Console.WriteLine(
"Don't need to be rotated...");

}
}

 

代码
class Program
{
static void Main(string[] args)
{
Console.WriteLine(
"***** Delegates as Parameters *****\n");

Garage g
= new Garage();

ServiceDepartment sd
= new ServiceDepartment();

// 洗车
g.ProcessCars(new Car.CarMaintenanceDelegate(sd.WashCar));

// 换胎
g.ProcessCars(new Car.CarMaintenanceDelegate(sd.RotateTires));

Console.ReadLine();
}
}

 

  以上我们看到了3个委托的实际示例.第一个实例(SimpleDelegate)演示了定义和操作委托类型的基础.委托只是维护了一个方法列表,之后我们可以(隐式或者显示)调用Invoke()方法来调用它.第二个示例(CarDelegete)演示了委托如何用于创建事件架构(代替.NET接口类型).最后,我们刚完成的那个示例支出,委托可以像其他.NET类型一样用于方法参数.
  总的来说,这3个实例说明了委托类型的核心细节.