收藏 分享(赏)

C#编程指南-委托.doc

上传人:gnk289057 文档编号:9468558 上传时间:2019-08-09 格式:DOC 页数:24 大小:75.50KB
下载 相关 举报
C#编程指南-委托.doc_第1页
第1页 / 共24页
C#编程指南-委托.doc_第2页
第2页 / 共24页
C#编程指南-委托.doc_第3页
第3页 / 共24页
C#编程指南-委托.doc_第4页
第4页 / 共24页
C#编程指南-委托.doc_第5页
第5页 / 共24页
点击查看更多>>
资源描述

1、C#编程指南-委托委托是一种安全地封装方法的类型,它与 C 和 C+ 中的函数指针类似。与 C 中的函数指针不同,委托是面向对象的、类型安全的和保险的。委托的类型由委托的名称定义。下面的示例声明了一个名为 Del 的委托,该委托可以封装一个采用字符串作为参数并返回 void 的方法。C# 复制代码 public delegate void Del(string message);构造委托对象时,通常提供委托将包装的方法的名称或使用匿名方法。实例化委托后,委托将把对它进行的方法调用传递给方法。调用方传递给委托的参数被传递给方法,来自方法的返回值(如果有)由委托返回给调用方。这被称为调用委托。可以

2、将一个实例化的委托视为被包装的方法本身来调用该委托。例如: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 类。委托类

3、型是密封的,不能从 Delegate 中派生委托类型,也不可能从中派生自定义类。由于实例化委托是一个对象,所以可以将其作为参数进行传递,也可以将其赋值给属性。这样,方法便可以将一个委托作为参数来接受,并且以后可以调用该委托。这称为异步回调,是在较长的进程完成后用来通知调用方的常用方法。以这种方式使用委托时,使用委托的代码无需了解有关所用方法的实现方面的任何信息。此功能类似于接口所提供的封装。有关更多信息,请参见何时使用委托而不使用接口。回调的另一个常见用法是定义自定义的比较方法并将该委托传递给排序方法。它允许调用方的代码成为排序算法的一部分。下面的示例方法使用 Del 类型作为参数:C# 复制

4、代码 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 的作用只

5、是准备字符串并将该字符串传递给其他方法。此功能特别强大,因为委托的方法可以使用任意数量的参数。将委托构造为包装实例方法时,该委托将同时引用实例和方法。除了它所包装的方法外,委托不了解实例类型,所以只要任意类型的对象中具有与委托签名相匹配的方法,委托就可以引用该对象。将委托构造为包装静态方法时,它只引用方法。考虑下列声明:C# 复制代码 public class MethodClasspublic void Method1(string message) public void Method2(string message) 加上前面显示的静态 DelegateMethod,现在我们有三个方法可

6、由 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 +=

7、 d3;此时,allMethodsDelegate 在其调用列表中包含三个方法 - Method1、Method2 和 DelegateMethod。原来的三个委托 d1、d2 和 d3 保持不变。调用 allMethodsDelegate 时,将按顺序调用所有这三个方法。如果委托使用引用参数,则引用将依次传递给三个方法中的每个方法,由一个方法引起的更改对下一个方法是可见的。如果任一方法引发了异常,而在该方法内未捕获该异常,则该异常将传递给委托的调用方,并且不再对调用列表中后面的方法进行调用。如果委托具有返回值和/或输出参数,它将返回最后调用的方法的返回值和参数。若要从调用列表中移除方法,请使

8、用减法运算符或减法赋值运算符(“-”或“-=” ) 。例如:C# 复制代码 /remove Method1allMethodsDelegate -= d1;/ copy AllMethodsDelegate while removing d2Del oneMethodDelegate = allMethodsDelegate - d2;由于委托类型派生自 System.Delegate,所以可在委托上调用该类定义的方法和属性。例如,为了找出委托的调用列表中的方法数,您可以编写下面的代码:C# 复制代码 int invocationCount = d1.GetInvocationList().G

9、etLength(0);在调用列表中具有多个方法的委托派生自 MulticastDelegate,这是 System.Delegate 的子类。由于两个类都支持 GetInvocationList,所以上面的代码在两种情况下都适用。多路广播委托广泛用于事件处理中。事件源对象向已注册接收该事件的接收方对象发送事件通知。为了为事件注册,接收方创建了旨在处理事件的方法,然后为该方法创建委托并将该委托传递给事件源。事件发生时,源将调用委托。然后,委托调用接收方的事件处理方法并传送事件数据。给定事件的委托类型由事件源定义。有关更多信息,请参见事件(C# 编程指南) 。在编译时,对分配了两种不同类型的委托

10、进行比较将产生编译错误。如果委托实例静态地属于类型 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

11、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;

12、这被称为使用命名的方法。使用命名方法构造的委托可以封装静态方法或实例方法。在早期版本的 C# 中,命名方法是对委托进行实例化的唯一方式。但是,在不希望付出创建新方法的系统开销时,C# 使您可以对委托进行实例化,并立即指定委托在被调用时将处理的代码块。这些被称为匿名方法(C# 编程指南) 。备注作为委托参数传递的方法必须与委托声明具有相同的签名。委托实例可以封装静态或实例方法。尽管委托可以使用 out 参数,但建议您不要将其用于多路广播事件委托,因为您无法知道哪个委托将被调用。示例 1以下是声明及使用委托的一个简单示例。注意,委托 Del 和关联的方法 MultiplyNumbers 具有相同的

13、签名 C# 复制代码 / Declare a delegatedelegate void Del(int i, double j);class MathClassstatic 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 MultiplyNumber

14、s:“);for (int i = 1; i )。IComparable 声明 CompareTo 方法,该方法返回一个整数,指定相同类型的两个对象之间的小于、等于或大于关系。IComparable 可用作排序算法的基础。虽然将委托比较方法用作排序算法的基础是有效的,但是并不理想。因为进行比较的能力属于类,而比较算法不会在运行时改变,所以单一方法接口是理想的。四、委托中的协变和逆变将方法签名与委托类型匹配时,协变和逆变可以提供一定程度的灵活性。协变允许方法具有的派生返回类型比委托中定义的更多。逆变允许方法具有的派生参数类型比委托类型中的更少。示例 1(协变)说明本示例演示如何将委托与具有返回类

15、型的方法一起使用,这些返回类型派生自委托签名中的返回类型。由 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;st

16、atic void Main()HandlerMethod handler1 = FirstHandler;/ Covariance allows this delegate.HandlerMethod handler2 = SecondHandler;示例 2(逆变)说明本示例演示如何将委托与具有某个类型的参数的方法一起使用,这些参数是委托签名参数类型的基类型。通过逆变,以前必须使用若干个不同处理程序的地方现在只要使用一个事件处理程序即可。如,现在可以创建一个接收 EventArgs 输入参数的事件处理程序,然后,可以将该处理程序与发送 MouseEventArgs 类型(作为参数)的 Bu

17、tton.MouseClick 事件一起使用,也可以将该处理程序与发送 KeyEventArgs 参数的 TextBox.KeyDown 事件一起使用。代码C# 复制代码 System.DateTime lastActivity;public Form1()InitializeComponent();lastActivity = new System.DateTime();this.textBox1.KeyDown += this.MultiHandler; /works with KeyEventArgsthis.button1.MouseClick += this.MultiHandler

18、; /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;五、如何:合并委托(多路广播委托)本示例演示如何组合多路广播委托。委托对象的一个用途在于,可以使用 + 运算符将它们分配给一个要成为多路广播委托的委托实例。组合的委托可调用组成它

19、的那两个委托。只有相同类型的委托才可以组合。- 运算符可用来从组合的委托移除组件委托。示例C# 复制代码 delegate void Del(string s);class TestClassstatic 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

20、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.Cons

21、ole.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:He

22、llo, 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 表达式来声明和

23、实例化委托。有关更多信息,请参见 Lambda 表达式(C# 编程指南) 。下面的示例阐释声明、实例化和使用委托。BookDB 类封装一个书店数据库,它维护一个书籍数据库。它公开 ProcessPaperbackBooks 方法,该方法在数据库中查找所有平装书,并对每本平装书调用一个委托。使用的 delegate 类型名为 ProcessBookDelegate。Test 类使用该类打印平装书的书名和平均价格。委托的使用促进了书店数据库和客户代码之间功能的良好分隔。客户代码不知道书籍的存储方式和书店代码查找平装书的方式。书店代码也不知道找到平装书后将对平装书执行什么处理。示例C# 复制代码 /

24、 A set of classes for handling a bookstore:namespace Bookstoreusing System.Collections;/ Describes a book in the book list:public struct Bookpublic 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 pa

25、perback?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 o

26、f 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 Pr

27、ocessPaperbackBooks(ProcessBookDelegate processBook)foreach (Book b in list)if (b.Paperback)/ Calling the delegate:processBook(b);/ Using the Bookstore classes:namespace BookTestClientusing Bookstore;/ Class to total and average prices of books:class PriceTotallerint countBooks = 0;decimal priceBook

28、s = 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)

29、;/ 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.PrintTitl

30、e: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(tot

31、aller.AddBookToTotal);System.Console.WriteLine(“Average Paperback Book Price: $0:#.#“,totaller.AveragePrice();/ Initialize the book database with some test books:static void AddBooks(BookDB bookDB)bookDB.AddBook(“The C Programming Language“, “Brian W. Kernighan and Dennis M. Ritchie“, 19.95m, true);

32、bookDB.AddBook(“The Unicode Standard 2.0“, “The Unicode Consortium“, 39.95m, true);bookDB.AddBook(“The MS-DOS Encyclopedia“, “Ray Duncan“, 129.95m, false);bookDB.AddBook(“Dogberts Clues for the Clueless“, “Scott Adams“, 12.00m, true);复制代码 Paperback Book Titles:The C Programming LanguageThe Unicode S

33、tandard 2.0Dogberts Clues for the CluelessAverage Paperback Book Price: $23.97可靠编程声明委托。下面的语句声明一个新的委托类型。C# 复制代码 public delegate void ProcessBookDelegate(Book book);每个委托类型都描述参数的数目和类型,以及它可以封装的方法的返回值类型。每当需要一组新的参数类型或新的返回值类型时,都必须声明一个新的委托类型。实例化委托。声明了委托类型后,必须创建委托对象并使之与特定方法关联。在上一个示例中,您通过按下面示例中的方式将 PrintTitle

34、 方法传递到 ProcessPaperbackBooks 方法来实现这一点:C# 复制代码 bookDB.ProcessPaperbackBooks(PrintTitle);这将创建与静态方法 Test.PrintTitle 关联的新委托对象。类似地,对象 totaller 的非静态方法 AddBookToTotal 是按下面示例中的方式传递的:C# 复制代码 bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);在两个示例中,都向 ProcessPaperbackBooks 方法传递了一个新的委托对象。委托创建后,它的关联方法就不能更改;委托对象是不可变的。调用委托。创建委托对象后,通常将委托对象传递给将调用该委托的其他代码。通过委托对象的名称(后面跟着要传递给委托的参数,括在括号内)调用委托对象。下面是委托调用的示例:C# 复制代码 processBook(b);与本例一样,可以通过使用 BeginInvoke 和 EndInvoke 方法同步或异步调用委托。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报