本教程演示委托類型。它說明如何將委托映射到靜態方法和實例方法,以及如何組合委托(多路廣播)。
C# 中的委托類似於 C 或 C++ 中的函數指針。使用委托使程序員可以將方法引用封裝在委托對象內。然後可以將該委托對象傳遞給可調用所引用方法的代碼,而不必在編譯時知道將調用哪個方法。與 C 或 C++ 中的函數指針不同,委托是面向對象、類型安全的,並且是安全的。
委托聲明定義一種類型,它用一組特定的參數以及返回類型封裝方法。對於靜態方法,委托對象封裝要調用的方法。對於實例方法,委托對象同時封裝一個實例和該實例上的一個方法。如果您有一個委托對象和一組適當的參數,則可以用這些參數調用該委托。
委托的一個有趣且有用的屬性是,它不知道或不關心自己引用的對象的類。任何對象都可以;只是方法的參數類型和返回類型必須與委托的參數類型和返回類型相匹配。這使得委托完全適合“匿名”調用。
注意 委托是在調用方的安全權限下運行而不是聲明方的權限下運行。
此教程包括兩個示例:
此外,還討論以下主題:
下面的示例闡釋聲明、實例化和使用委托。BookDB
類封裝一個書店數據庫,它維護一個書籍數據庫。它公開 ProcessPaperbackBooks
方法,該方法在數據庫中查找所有平裝書,並為每本書調用一個委托。所使用的 delegate 類型稱為 ProcessBookDelegate
。Test
類使用該類輸出平裝書的書名和平均價格。
委托的使用促進了書店數據庫和客戶代碼之間功能的良好分隔。客戶代碼不知道書籍的存儲方式和書店代碼查找平裝書的方式。書店代碼也不知道找到平裝書後將對平裝書進行什麼處理。
// bookstore.cs using System; // 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 Test { // Print the title of the book. static void PrintTitle(Book b) { 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: Console.WriteLin[1] [2] [3] 下一頁
e("Paperback Book Titles:"); // Create a new delegate object associated with the static // method Test.PrintTitle: bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(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(new ProcessBookDelegate(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); } } }
Paperback Book Titles: The C Programming Language The Unicode Standard 2.0 Dogbert's Clues for the Clueless Average Paperback Book Price: $23.97
public delegate void ProcessBookDelegate(Book book);
聲明一個新的委托類型。每個委托類型都描述參數的數目和類型,以及它可以封裝的方法的返回值類型。每當需要一組新的參數類型或新的返回值類型時,都必須聲明一個新的委托類型。
下列語句:
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));
創建與靜態方法 Test.PrintTitle
關聯的新的委托對象。下列語句:
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal));
創建與對象 totaller
上的非靜態方法 AddBookToTotal
關聯的新的委托對象。在兩個例子中,新的委托對象都立即傳遞給 ProcessPaperbackBooks
方法。
請注意一旦創建了委托,它所關聯到的方法便永不改變:委托對象不可改變。
processBook(b);
在此示例中,可以通過使用 BeginInvoke 和 EndInvoke 方法同步或異步調用委托。
本示例演示組合委托。委托對象的一個有用屬性是,它們可以“+”運算符來組合。組合的委托可調用組成它的那兩個委托。只有相同類型的委托才可以組合。
“-”運算符可用來從組合的委托移除組件委托。
// compose.cs using System; delegate void MyDelegate(string s); class MyClass { public static void Hello(string s) { Console.WriteLine(" Hello, {0}!", s); } public static void Goodbye(string s) { Console.WriteLine(" Goodbye, {0}!", s); } public static void Main() { MyDelegate a, b, c, d; // Create the delegate object a that references // the method Hello: a = new MyDelegate(Hello); // Create the delegate object b that references // the method Goodbye: b = new MyDelegate(Goodbye); // The two delegates, a and b, a上一頁 [1] [2] [3] 下一頁
re composed to form c: c = a + b; // Remove a from the composed delegate, leaving d, // which calls only the method Goodbye: d = c - a; Console.WriteLine("Invoking delegate a:"); a("A"); Console.WriteLine("Invoking delegate b:"); b("B"); Console.WriteLine("Invoking delegate c:"); c("C"); 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!
委托非常適合於用作事件(從一個組件就該組件中的更改通知“偵聽器”)。有關將委托用於事件的更多信息,請參見事件教程。
委托和接口的類似之處是,它們都允許分隔規范和實現。多個獨立的作者可以生成與一個接口規范兼容的多個實現。類似地,委托指定方法的簽名,多個作者可以編寫與委托規范兼容的多個方法。何時應使用接口,而何時應使用委托呢?
委托在以下情況下很有用:
接口在以下情況下很有用:
上一頁 [1] [2] [3]