委託是回調函數的類型安全包裝。C++編寫的非託管程序進行回調時很容易出錯。由於委託的存在,託管應用程序不會出現這樣的情況。委託通常用來定義響應事件的回調方法的簽名。
C#中的委託類似於C或C++中的函數指針。使用委託使程序員可以將方法引用封裝在委託對象內( 所以這裏的“引用”不是原始內存地址,而是包裝了方法的內存地址的委託實例 )。然後可以將給委託對象傳遞可調用所引用方法的代碼,而不必在編譯時知道將調用哪個方法。與C或C++中的函數指針不同,委託是面向對象、類型安全的,並且是安全的。
委託聲明定義一種類型,它用一組特定的參數以及返回類型封裝方法。
對於靜態方法,委託對象封裝要調用的方法。
對於實例方法,委託對象同時封裝一個實例和該實例上的一個方法。
如果你有一個委託對象和一組適當的參數,則可以用這些參數調用該委託。
委託的一個有趣且有用的屬性是: 它不知道或不關心自己引用的對象的類。任何對象都可以,知識方法的參數類型必須與委託的參數類型和返回類型相匹配。這是的委託完全適合“匿名”調用。
到現在已經說得不少了,現在開始用例子說明如何聲明、實例化和調用委託:下面的示例中,BookDB 類封裝一個書店數據庫,它維護一個書籍數據庫。它公開 ProcessPaperbackBooks 方法,該方法在數據庫中查找所有平裝書,併爲每本書調用一個委託。所使用的 delegate 類型稱爲 ProcessBookDelegate.Test 類使用該類輸出平裝書的書名和平均價格。委託的使用促進了書店數據庫和客戶代碼之間功能的良好分隔。客戶代碼不知道書籍的存儲方式和書店代碼查找平裝書的方式。書店代碼也不知道找到平裝書後將對平裝書進行什麼處理。
using System;
//此命名空間中的幾個類用以維護書籍數據庫:
namespace Bookstore
...{
using System.Collections;
// 描述數據庫中每一本書具有的屬性:
public struct Book
...{
public string Title; // 書的題目.
public string Author; // 書的作者.
public decimal Price; // 書的價格.
public bool Paperback; // 是不是平裝書?
public Book(string title, string author, decimal price, bool paperBack)
...{
Title = title;
Author = author;
Price = price;
Paperback = paperBack;
}
}
// 爲處理平裝書的類聲明一個委託類型(delegate類型):
public delegate void ProcessBookDelegate(Book book);
// 維護書籍的數據庫.
public class BookDB
...{
// 數據庫中所有書籍的清單:
ArrayList list = new ArrayList();
// 向數據庫中添加一本書:
public void AddBook(string title, string author, decimal price, bool paperBack)
...{
list.Add(new Book(title, author, price, paperBack));
}
// 對於每一本平裝書調用ProcessBookDelegate這個委託,以對平裝書進行處理:
public void ProcessPaperbackBooks(ProcessBookDelegate processBook)
...{
foreach (Book b in list)
...{
if (b.Paperback)
// Calling the delegate:
processBook(b);
}
}
}
}
// 使用Bookstore命名空間裏的類:
namespace BookTestClient
...{
using Bookstore;
// 具有統計平裝書並求出平裝書的平均價格功能的類:
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 Test
...{
// 打印書籍題目的靜態方法.
static void PrintTitle(Book b)
...{
Console.WriteLine(" {0}", b.Title);
}
// 程序執行的入口:
static void Main()
...{
BookDB bookDB = new BookDB();
// 用幾本書來輸初始化書籍數據庫:
AddBooks(bookDB);
// 打印平裝書的題目:
Console.WriteLine("Paperback Book Titles:");
// 創建與靜態方法 Test.PrintTitle 關聯的新的委託對象:
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));
// 通過PriceTotaller的實例獲取平裝書的平均價格:
PriceTotaller totaller = new PriceTotaller();
// 創建與對象 totaller 上的非靜態方法 AddBookToTotal 關聯的新的委託對象:
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal));
Console.WriteLine("平裝書的平均價格: ${0:#.##}",
totaller.AveragePrice());
}
// 向數據庫添加書以初始化書籍數據庫:
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
平裝書的平均價格: $23.97
public delegate void ProcessBookDelegate(Book book); |
聲明一個新的委託類型。每個委託類型都描述參數的數目和類型,以及它可以封裝的方法的返回值類型。每當需要一組新的參數類型或新的返回值類型時,都必須聲明一個新的委託類型。
實例化委託 聲明瞭委託類型後,必須創建委託對象並使之與特定方法關聯。與所有其他對象類似,新的委託對象用 new 表達式創建。但是當創建委託時,傳遞給 new 表達式的參數很特殊:它的編寫類似於方法調用,但沒有方法的參數。下列語句:
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle)); |
創建與靜態方法 Test.PrintTitle 關聯的新的委託對象。下列語句:
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal)); |
創建與對象 totaller 上的非靜態方法 AddBookToTotal 關聯的新的委託對象。在例子中,新的委託對象都立即傳遞給 ProcessPaperbackBooks 方法。
請注意一旦創建了委託,它所關聯到的方法便永不改變:委託對象不可改變。
調用委託 創建委託對象後,通常將委託對象傳遞給將調用該委託的其他代碼。通過委託對象的名稱(後面跟着要傳遞給委託的參數,括在括號內)調用委託對象。下面是委託調用的示例:
processBook(b); |