发布:2023/12/7 15:48:53作者:大数据 来源:大数据 浏览次数:448
委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。 在实例化委托时,你可以将其实例与任何具有兼容签名和返回类型的方法相关联。 你可以通过委托实例调用方法。
委托用于将方法作为参数传递给其他方法。 事件处理程序就是通过委托调用的方法。 你可以创建一个自定义方法,当发生特定事件时,某个类(如 Windows 控件)就可以调用你的方法。 下面的示例演示了一个委托声明:
C#
1 |
public delegate int PerformCalculation(int x, int y); |
可将任何可访问类或结构中与委托类型匹配的任何方法分配给委托。 该方法可以是静态方法,也可以是实例方法。 此灵活性意味着你可以通过编程方式来更改方法调用,还可以向现有类中插入新代码。
备注
在方法重载的上下文中,方法的签名不包括返回值。 但在委托的上下文中,签名包括返回值。 换句话说,方法和委托必须具有相同的返回类型。
将方法作为参数进行引用的能力使委托成为定义回调方法的理想选择。 可编写一个比较应用程序中两个对象的方法。 该方法可用在排序算法的委托中。 由于比较代码与库分离,因此排序方法可能更常见。
对于类似的方案,已将函数指针添加到 C# 9,其中你需要对调用约定有更多的控制。 使用添加到委托类型的虚方法调用与委托关联的代码。 使用函数指针,可以指定不同的约定。
委托具有以下属性:
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/delegates/how-to-declare-instantiate-and-use-a-delegate
在 C# 1.0 和更高版本中,可以如下面的示例所示声明委托。
C#
1 2 3 4 5 6 7 8 |
// Declare a delegate. delegate void Del(string str); // Declare a method with the same signature as the delegate. static void Notify(string name) { Console.WriteLine($"Notification received for: {name}"); } |
C#
1 2 |
// Create an instance of the delegate. Del del1 = new Del(Notify); |
C# 2.0 提供了更简单的方法来编写前面的声明,如下面的示例所示。
C#
1 2 |
// C# 2.0 provides a simpler way to declare an instance of Del. Del del2 = Notify; |
在 C# 2.0 和更高版本中,还可以使用匿名方法来声明和初始化委托,如下面的示例所示。
C#
1 2 3 |
// Instantiate Del by using an anonymous method. Del del3 = delegate(string name) { Console.WriteLine($"Notification received for: {name}"); }; |
在 C# 3.0 和更高版本中,还可以通过使用 lambda 表达式声明和实例化委托,如下面的示例所示。
C#
1 |
1 2 |
// Instantiate Del by using a lambda expression. Del del4 = name => { Console.WriteLine($"Notification received for: {name}"); }; |
1 |
有关详细信息,请参阅 Lambda 表达式。
下面的示例演示如何声明、实例化和使用委托。 BookDB
类封装用来维护书籍数据库的书店数据库。 它公开一个方法 ProcessPaperbackBooks
,用于在数据库中查找所有平装书并为每本书调用委托。 使用的 delegate
类型名为 ProcessBookCallback
。 Test
类使用此类打印平装书的书名和平均价格。
使用委托提升书店数据库和客户端代码之间的良好分隔功能。 客户端代码程序不知道如何存储书籍或书店代码如何查找平装书。 书店代码不知道它在找到平装书之后对其执行什么处理。
C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
// 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 ProcessBookCallback(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(ProcessBookCallback 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 Test { // Print the title of the book. static void PrintTitle(Book b) { Console.WriteLine($" {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: 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); 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); 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("Dogbert's Clues for the Clueless", "Scott Adams", 12.00m, true); } } } /* Output: Paperback Book Titles: The C Programming Language The Unicode Standard 2.0 Dogbert's Clues for the Clueless Average Paperback Book Price: $23.97 */ |
C#
1 |
public delegate void ProcessBookCallback(Book book); |
每个委托类型描述自变量的数量和类型,以及它可以封装的方法的返回值类型。 每当需要一组新的自变量类型或返回值类型,则必须声明一个新的委托类型。
PrintTitle
方法传递给 ProcessPaperbackBooks
方法执行此操作,如下面的示例所示:
C#
1 |
bookDB.ProcessPaperbackBooks(PrintTitle); |
这将创建一个新的与静态方法 关联的委托对象。 同样,如下面的示例所示,传递对象 totaller
中的非静态方法 AddBookToTotal
:
C#
1 |
bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal); |
在这两种情况下,都将新的委托对象传递给 ProcessPaperbackBooks
方法。
创建委托后,它与之关联的方法就永远不会更改;委托对象是不可变的。
C#
1 |
processBook(b); |
委托可以同步调用(如在本例中)或通过使用 BeginInvoke
和 EndInvoke
方法异步调用。
© Copyright 2014 - 2024 柏港建站平台 ejk5.com. 渝ICP备16000791号-4