.NET 委托

委托类型定义  C#编 译器处理委托时,先自动产生一个派生自System.MulticastDelegate的密封类。这个类与它的基类System.Delegate一起 为委托提供必要的基础设施,以维护以后将要调用的方法列表。它含有3个编译器生成的方法,这3个方法的参数与返回值基于委托的声明。

public sealed class DelegateName :System.MulticastDelegate
{
    public DReturnType Invoke (DParams);
    public IAsyncResult BeginInvoke(DParams,AsyncCallback cb,object state);
    public DReturnType  EndInvoke(DRefAndOutParams,IAsyncResult result);
}

  委托类型实例
  某种类型的实例方法和可分配给该类型的目标对象。
  某种类型的实例方法(包含在形参表中公开的隐藏 this 参数)。该委托称为开放式实例委托。
  静态方法。
  静态方法和可分配给该方法的第一个参数的目标对象。该委托称为通过其第一个参数关闭。
  何时使用委托而不使用接口
  委托和接口都允许类设计器分离类型声明和实现。给定的接口可由任何类或结构继承和实现;可以为任何类中的方法创建委托,前提是该方法符合委托的 方法签名。接口引用或委托可由不了解实现该接口或委托方法的类的对象使用。既然存在这些相似性,那么类设计器何时应使用委托,何时又该使用接口呢?
  在以下情况中使用委托
  当使用事件设计模式时。
  当封装静态方法可取时。
  当调用方不需要访问实现该方法的对象中的其他属性、方法或接口时。
  需要方便的组合。
  当类可能需要该方法的多个实现时。
  在以下情况中使用接口
  当存在一组可能被调用的相关方法时。
  当类只需要方法的单个实现时。
  当使用接口的类想要将该接口强制转换为其他接口或类类型时。
  当正在实现的方法链接到类的类型或标识时:例如比较方法。
  声明委托
  声明一个新的委托类型。每个委托类型都描述参数的数目和类型,以及它可以封装的方法的返回值类型。每当需要一组新的参数类型或新的返回值类型时,都必须声明一个新的委托类型。

public delegate void ProcessBookDelegate(Book book); 

  实例化委托
  声明了委托类型后,必须创建委托对象并使之与特定方法关联。
  在上面的示例中,这是通过将 PrintTitle 方法传递给ProcessPaperbackBooks 方法来完成的。

bookDB.ProcessPaperbackBooks(PrintTitle); 

  这将创建与静态方法 Test.PrintTitle 关联的新委托对象。类似地,对象 totaller 的非静态方法 AddBookToTotal 是按如下方式传递的

bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal); 

  调用委托
  创建委托对象后,通常将委托对象传递给将调用该委托的其他代码。通过委托对象的名称(后面跟着要传递给委托的参数,括在括号内)调用委托对象。

processBook(b); 

  多路广播委托
  本示例演示如何组合多路广播委托。委托对象的一个用途在于,可以使用 + 运算符将它们分配给一个要成为多路广播委托的委托实例。组合的委托可调用组成它的那两个委托。只有相同类型的委托才可以组合。- 运算符可用来从组合的委托移除组件委托。
  根据CIL代码可以发现,+=操作符实际上是转换为了一个静态的Delegate.Combine()方法的调用。-=操作符实际上是转换为了一个静态的Delegate.Remove()方法的调用。

delegate void Del(string s); class TestClass { static void Hello(string s) { System.Console.WriteLine(" Hello, {0}!", s); } static void Goodbye(string s) { System.Console.WriteLine(" Goodbye, {0}!", s); } static void Main() { Del a, b, c, d; a = Hello; b = Goodbye; c = a + b; d = c - a; System.Console.WriteLine("Invoking delegate a:"); a("A"); System.Console.WriteLine("Invoking delegate b:"); b("B"); System.Console.WriteLine("Invoking delegate c:"); c("C"); System.Console.WriteLine("Invoking delegate d:"); d("D"); } } 

  匿名方法
  在 2.0 之前的 C# 版本中,声明委托的唯一方法是使用命名方法。C# 2.0 引入了匿名方法。要将代码块传递为委托参数,创建匿名方法则是唯一的方法。
  如果使用匿名方法,则不必创建单独的方法,因此减少了实例化委托所需的编码系统开销。例如,如果创建方法所需的系统开销是不必要的,在委托的位 置指定代码块就非常有用。启动新线程即是一个很好的示例。无需为委托创建更多方法,线程类即可创建一个线程并且包含该线程执行的代码。

button1.Click += delegate(System.Object o, System.EventArgs e) { System.Windows.Forms.MessageBox.Show("Click!"); }; 
delegate void Del(int x); Del d = delegate(int k) { /* ... */ }; 
void StartThread() { System.Threading.Thread t1 = new System.Threading.Thread (delegate() { System.Console.Write("Hello, "); System.Console.WriteLine("World!"); }); t1.Start(); } 

  匿名方法可以访问定义他们的方法的本地变量,即匿名方法的外部变量。
  匿名方法不能访问定义方法中的ref或out参数
  匿名方法中的本地变量不能与外部方法的本地变量重名
  匿名方法可以访问外部类作用域中的实例变量或静态变量
  委托中的协变和逆变
  将方法签名与委托类型匹配时,协变和逆变为您提供了一定程度的灵活性。
  协变也称为宽松委托,协变允许构造一个委托,指向返回类及其相关继承体系的方法。
  逆变允许方法具有的派生参数类型比委托类型中的更少。