




已阅读5页,还剩11页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C#编程指南-委托.txt-/自私,让我们只看见自己却容不下别人。如果发短信给你喜欢的人,他不回,不要再发。看着你的相片,我就特冲动的想P成黑白挂墙上!有时,不是世界太虚伪,只是,我们太天真。一、使用委托委托是一种安全地封装方法的类型,它与 C 和 C+ 中的函数指针类似。与 C 中的函数指针不同,委托是面向对象的、类型安全的和保险的。委托的类型由委托的名称定义。下面的示例声明了一个名为 Del 的委托,该委托可以封装一个采用字符串作为参数并返回 void 的方法。C# 复制代码 public delegate void Del(string message); 构造委托对象时,通常提供委托将包装的方法的名称或使用匿名方法。实例化委托后,委托将把对它进行的方法调用传递给方法。调用方传递给委托的参数被传递给方法,来自方法的返回值(如果有)由委托返回给调用方。这被称为调用委托。可以将一个实例化的委托视为被包装的方法本身来调用该委托。例如:C# 复制代码 / Create a method for a delegate.public static void DelegateMethod(string message) System.Console.WriteLine(message); C# 复制代码 / Instantiate the delegate.Del handler = DelegateMethod;/ Call the delegate.handler(Hello World); 委托类型派生自 .NET Framework 中的 Delegate 类。委托类型是密封的,不能从 Delegate 中派生委托类型,也不可能从中派生自定义类。由于实例化委托是一个对象,所以可以将其作为参数进行传递,也可以将其赋值给属性。这样,方法便可以将一个委托作为参数来接受,并且以后可以调用该委托。这称为异步回调,是在较长的进程完成后用来通知调用方的常用方法。以这种方式使用委托时,使用委托的代码无需了解有关所用方法的实现方面的任何信息。此功能类似于接口所提供的封装。有关更多信息,请参见何时使用委托而不使用接口。回调的另一个常见用法是定义自定义的比较方法并将该委托传递给排序方法。它允许调用方的代码成为排序算法的一部分。下面的示例方法使用 Del 类型作为参数:C# 复制代码 public void MethodWithCallback(int param1, int param2, Del callback) callback(The number is: + (param1 + param2).ToString(); 然后可以将上面创建的委托传递给该方法:C# 复制代码 MethodWithCallback(1, 2, handler); 在控制台中将收到下面的输出:The number is: 3 在将委托用作抽象概念时,MethodWithCallback 不需要直接调用控制台 - 设计它时无需考虑控制台。MethodWithCallback 的作用只是准备字符串并将该字符串传递给其他方法。此功能特别强大,因为委托的方法可以使用任意数量的参数。将委托构造为包装实例方法时,该委托将同时引用实例和方法。除了它所包装的方法外,委托不了解实例类型,所以只要任意类型的对象中具有与委托签名相匹配的方法,委托就可以引用该对象。将委托构造为包装静态方法时,它只引用方法。考虑下列声明:C# 复制代码 public class MethodClass public void Method1(string message) public void Method2(string message) 加上前面显示的静态 DelegateMethod,现在我们有三个方法可由 Del 实例进行包装。调用委托时,它可以调用多个方法。这称为多路广播。若要向委托的方法列表(调用列表)中添加额外的方法,只需使用加法运算符或加法赋值运算符(“+”或“+=”)添加两个委托。例如:C# 复制代码 MethodClass obj = new MethodClass();Del d1 = obj.Method1;Del d2 = obj.Method2;Del d3 = DelegateMethod;/Both types of assignment are valid.Del allMethodsDelegate = d1 + d2;allMethodsDelegate += d3; 此时,allMethodsDelegate 在其调用列表中包含三个方法 - Method1、Method2 和 DelegateMethod。原来的三个委托 d1、d2 和 d3 保持不变。调用 allMethodsDelegate 时,将按顺序调用所有这三个方法。如果委托使用引用参数,则引用将依次传递给三个方法中的每个方法,由一个方法引起的更改对下一个方法是可见的。如果任一方法引发了异常,而在该方法内未捕获该异常,则该异常将传递给委托的调用方,并且不再对调用列表中后面的方法进行调用。如果委托具有返回值和/或输出参数,它将返回最后调用的方法的返回值和参数。若要从调用列表中移除方法,请使用减法运算符或减法赋值运算符(“-”或“-=”)。例如:C# 复制代码 /remove Method1allMethodsDelegate -= d1;/ copy AllMethodsDelegate while removing d2Del oneMethodDelegate = allMethodsDelegate - d2; 由于委托类型派生自 System.Delegate,所以可在委托上调用该类定义的方法和属性。例如,为了找出委托的调用列表中的方法数,您可以编写下面的代码:C# 复制代码 int invocationCount = d1.GetInvocationList().GetLength(0); 在调用列表中具有多个方法的委托派生自 MulticastDelegate,这是 System.Delegate 的子类。由于两个类都支持 GetInvocationList,所以上面的代码在两种情况下都适用。多路广播委托广泛用于事件处理中。事件源对象向已注册接收该事件的接收方对象发送事件通知。为了为事件注册,接收方创建了旨在处理事件的方法,然后为该方法创建委托并将该委托传递给事件源。事件发生时,源将调用委托。然后,委托调用接收方的事件处理方法并传送事件数据。给定事件的委托类型由事件源定义。有关更多信息,请参见事件(C# 编程指南)。在编译时,对分配了两种不同类型的委托进行比较将产生编译错误。如果委托实例静态地属于类型 System.Delegate,则允许进行比较,但在运行时将返回 false。例如:C# 复制代码 delegate void Delegate1();delegate void Delegate2();static void method(Delegate1 d, Delegate2 e, System.Delegate f) / Compile-time error. /Console.WriteLine(d = e); / OK at compile-time. False if the run-time type of f /is not the same as that of d. System.Console.WriteLine(d = f);二、带有命名方法的委托与带有匿名方法的委托委托可以与命名方法关联。使用命名方法对委托进行实例化时,该方法将作为参数传递,例如:C# 复制代码 / Declare a delegate:delegate void Del(int x);/ Define a named method:void DoWork(int k) /* . */ / Instantiate the delegate using the method as a parameter:Del d = obj.DoWork; 这被称为使用命名的方法。使用命名方法构造的委托可以封装静态方法或实例方法。在早期版本的 C# 中,命名方法是对委托进行实例化的唯一方式。但是,在不希望付出创建新方法的系统开销时,C# 使您可以对委托进行实例化,并立即指定委托在被调用时将处理的代码块。这些被称为匿名方法(C# 编程指南)。备注作为委托参数传递的方法必须与委托声明具有相同的签名。委托实例可以封装静态或实例方法。尽管委托可以使用 out 参数,但建议您不要将其用于多路广播事件委托,因为您无法知道哪个委托将被调用。示例 1以下是声明及使用委托的一个简单示例。注意,委托 Del 和关联的方法 MultiplyNumbers 具有相同的签名 C# 复制代码 / Declare a delegatedelegate void Del(int i, double j);class MathClass static void Main() MathClass m = new MathClass(); / Delegate instantiation using MultiplyNumbers Del d = m.MultiplyNumbers; / Invoke the delegate object. System.Console.WriteLine(Invoking the delegate using MultiplyNumbers:); for (int i = 1; i = 5; i+) d(i, 2); / Declare the associated method. void MultiplyNumbers(int m, double n) System.Console.Write(m * n + ); 输出Invoking the delegate using MultiplyNumbers: 2 4 6 8 10 示例 2在下面的示例中,一个委托被同时映射到静态方法和实例方法,并分别返回特定的信息。C# 复制代码 / Declare a delegatedelegate void Del();class SampleClass public void InstanceMethod() System.Console.WriteLine(A message from the instance method.); static public void StaticMethod() System.Console.WriteLine(A message from the static method.); class TestSampleClass static void Main() SampleClass sc = new SampleClass(); / Map the delegate to the instance method: Del d = sc.InstanceMethod; d(); / Map to the static method: d = SampleClass.StaticMethod; d(); 输出A message from the instance method. A message from the static method. 三、何时使用委托而不使用接口委托和接口都允许类设计器分离类型声明和实现。任何类或结构都能继承和实现给定的接口。可以为任何类上的方法创建委托,前提是该方法符合委托的方法签名。接口引用或委托可由不了解实现该接口或委托方法的类的对象使用。既然存在这些相似性,那么类设计器何时应使用委托,何时又该使用接口呢?在以下情况下,请使用委托:当使用事件设计模式时。当封装静态方法可取时。当调用方不需要访问实现该方法的对象中的其他属性、方法或接口时。需要方便的组合。当类可能需要该方法的多个实现时。在以下情况下,请使用接口:当存在一组可能被调用的相关方法时。当类只需要方法的单个实现时。当使用接口的类想要将该接口强制转换为其他接口或类类型时。当正在实现的方法链接到类的类型或标识时:例如比较方法。使用单一方法接口而不使用委托的一个很好的示例是 IComparable 或泛型版本 IComparable(Of )。IComparable 声明 CompareTo 方法,该方法返回一个整数,指定相同类型的两个对象之间的小于、等于或大于关系。IComparable 可用作排序算法的基础。虽然将委托比较方法用作排序算法的基础是有效的,但是并不理想。因为进行比较的能力属于类,而比较算法不会在运行时改变,所以单一方法接口是理想的。四、委托中的协变和逆变将方法签名与委托类型匹配时,协变和逆变可以提供一定程度的灵活性。协变允许方法具有的派生返回类型比委托中定义的更多。逆变允许方法具有的派生参数类型比委托类型中的更少。示例 1(协变)说明本示例演示如何将委托与具有返回类型的方法一起使用,这些返回类型派生自委托签名中的返回类型。由 SecondHandler 返回的数据类型是 Dogs 类型,它是由委托中定义的 Mammals 类型派生的。代码C# 复制代码 class Mammalsclass Dogs : Mammalsclass Program / Define the delegate. public delegate Mammals HandlerMethod(); public static Mammals FirstHandler() return null; public static Dogs SecondHandler() return null; static void Main() HandlerMethod handler1 = FirstHandler; / Covariance allows this delegate. HandlerMethod handler2 = SecondHandler; 示例 2(逆变)说明本示例演示如何将委托与具有某个类型的参数的方法一起使用,这些参数是委托签名参数类型的基类型。通过逆变,以前必须使用若干个不同处理程序的地方现在只要使用一个事件处理程序即可。如,现在可以创建一个接收 EventArgs 输入参数的事件处理程序,然后,可以将该处理程序与发送 MouseEventArgs 类型(作为参数)的 Button.MouseClick 事件一起使用,也可以将该处理程序与发送 KeyEventArgs 参数的 TextBox.KeyDown 事件一起使用。代码C# 复制代码 System.DateTime lastActivity;public Form1() InitializeComponent(); lastActivity = new System.DateTime(); this.textBox1.KeyDown += this.MultiHandler; /works with KeyEventArgs this.button1.MouseClick += this.MultiHandler; /works with MouseEventArgs/ Event hander for any event with an EventArgs or/ derived class in the second parameterprivate void MultiHandler(object sender, System.EventArgs e) lastActivity = System.DateTime.Now; 五、如何:合并委托(多路广播委托)本示例演示如何组合多路广播委托。委托对象的一个用途在于,可以使用 + 运算符将它们分配给一个要成为多路广播委托的委托实例。组合的委托可调用组成它的那两个委托。只有相同类型的委托才可以组合。- 运算符可用来从组合的委托移除组件委托。示例C# 复制代码 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; / Create the delegate object a that references / the method Hello: a = Hello; / Create the delegate object b that references / the method Goodbye: b = Goodbye; / The two delegates, a and b, are composed to form c: c = a + b; / Remove a from the composed delegate, leaving d, / which calls only the method Goodbye: 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); 复制代码 Invoking delegate a: Hello, A!Invoking delegate b: Goodbye, B!Invoking delegate c: Hello, C! Goodbye, C!Invoking delegate d: Goodbye, D! 六、如何:声明、实例化和使用委托在 C# 1.0 及更高版本中,可以按此处所示方式声明委托:C# 复制代码 public delegate void Del(T item);public void Notify(int i) C# 复制代码 Del d1 = new Del(Notify); 在 C# 2.0 及更高版本中,还可以使用以下简化的语法,通过匿名方法来声明和初始化委托:C# 复制代码 Del d2 = Notify; 在 C# 3.0 及更高版本中,还可以使用 Lambda 表达式来声明和实例化委托。有关更多信息,请参见 Lambda 表达式(C# 编程指南)。下面的示例阐释声明、实例化和使用委托。BookDB 类封装一个书店数据库,它维护一个书籍数据库。它公开 ProcessPaperbackBooks 方法,该方法在数据库中查找所有平装书,并对每本平装书调用一个委托。使用的 delegate 类型名为 ProcessBookDelegate。Test 类使用该类打印平装书的书名和平均价格。委托的使用促进了书店数据库和客户代码之间功能的良好分隔。客户代码不知道书籍的存储方式和书店代码查找平装书的方式。书店代码也不知道找到平装书后将对平装书执行什么处理。示例C# 复制代码 / A set of classes for handling a bookstore:namespace Bookstore using System.Collections; / Describes a book in the book list: public struct Book public string Title; / Title of the book. public string Author; / Author of the book. public decimal Price; / Price of the book. public bool Paperback; / Is it paperback? public Book(string title, string author, decimal price, bool paperBack) Title = title; Author = author; Price = price; Paperback = paperBack; / Declare a delegate type for processing a book: public delegate void ProcessBookDelegate(Book book); / Maintains a book database. public class BookDB / List of all books in the database: ArrayList list = new ArrayList(); / Add a book to the database: public void AddBook(string title, string author, decimal price, bool paperBack) list.Add(new Book(title, author, price, paperBack); / Call a passed-in delegate on each paperback book to process it: public void ProcessPaperbackBooks(ProcessBookDelegate processBook) foreach (Book b in list) if (b.Paperback) / Calling the delegate: processBook(b); / Using the Bookstore classes:namespace BookTestClient using Bookstore; / Class to total and average prices of books: class PriceTotaller int countBooks = 0; decimal priceBooks = 0.0m; internal void AddBookToTotal(Book book) countBooks += 1; priceBooks += book.Price; internal decimal AveragePrice() return priceBooks / countBooks; / Class to test the book database: class TestBookDB / Print the title of the book. static void PrintTitle(Book b) System.Console.WriteLine( 0, b.Title); / Execution starts here. static void Main() BookDB bookDB = new BookDB(); / Initialize the database with some books: AddBooks(bookDB); / Print all the titles of paperbacks: System.Console.WriteLine(Paperback Book Titles:); / Create a new delegate object associated with the static / method Test.PrintTitle: bookDB.ProcessPaperbackBooks(PrintTitle); / Get the average price of a paperback by using / a PriceTotaller object: PriceTotaller totaller = new PriceTotaller(); / Create a new delegate object associated with the nonstatic / method AddBookToTotal on the object totaller: bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal); System.Console.WriteLine(Average Paperback Book Price: $0:#.#, totaller.AveragePrice(); / Initialize the book database with some test books: static void AddBooks(BookDB bo
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024-2025学年高中语文 第四单元 文言文(3)第15课 陈情表教学设计 粤教版必修5
- 18囊萤夜读教学设计-2023-2024学年四年级下册语文统编版
- Unit 2 Travelling around the world 第3课时Listening (p.24),Speaking (p.27) 教案 2024-2025学年沪教版(2024)七年级英语下册
- 2024-2025学年高中生物 第二章 基因和染色体的关系 第1节 减数分裂和受精作用 二 受精作用教学设计2 新人教版必修2
- 2024秋三年级英语上册 Unit 1 Hello Part B第一课时教学设计 人教PEP
- Module 9 Unit 1 Im going to do long jump. (教学设计)-2023-2024学年外研版(一起)英语三年级上册
- 5 国家机构有哪些 第二课时 国家机关的产生(教学设计)-部编版道德与法治六年级上册
- 11《军神》(教学设计)2023-2024学年统编版语文五年级下册
- 七年级地理下册 第七章 第4节《俄罗斯》教学设计 (新版)新人教版
- 三年级道德与法治下册 第四单元 多样的交通和通信 12 慧眼看交通教学设计2 新人教版
- 2025年超高功率大吨位电弧炉项目建议书
- 浙江省杭州市萧山区2022-2023学年第二学期四年级科学期中试题(含答案)
- 宠物殡葬创新创业
- 2024年黑龙江出版集团招聘笔试真题
- 2024年4月27日浙江省事业单位招聘考试《职业能力倾向测验》真题及答案
- 2025年上半年上海青浦新城发展(集团)限公司自主招聘9名易考易错模拟试题(共500题)试卷后附参考答案
- 小学数学教学中错题资源的有效利用研究论文
- 2025年山西电力职业技术学院单招职业技能考试题库及答案1套
- 2025年高考预测猜题 化学 信息必刷卷02(新高考 通 用)(解析版)
- 3.2依法行使权利 课件 -2024-2025学年统编版道德与法治八年级下册
- 2025年洛阳科技职业学院单招职业技能测试题库及答案(考点梳理)
评论
0/150
提交评论