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