C# 中的委托类似于 C 或 C++ 中的函数指针
使用委托使程序员可以将方法引用封装在委托对象内
然后可以将该委托对象传递给可调用所引用方法的代码
而不必在编译时知道将调用哪个方法
与 C 或 C++ 中的函数指针不同
委托是面向对象
类型安全的
并且是安全的
委托声明定义一种类型它用一组特定的参数以及返回类型封装方法对于静态方法委托对象封装要调用的方法对于实例方法委托对象同时封装一个实例和该实例上的一个方法如果您有一个委托对象和一组适当的参数则可以用这些参数调用该委托
委托的一个有趣且有用的属性是它不知道或不关心自己引用的对象的类任何对象都可以只是方法的参数类型和返回类型必须与委托的参数类型和返回类型相匹配这使得委托完全适合匿名调用
此源码下载>教程包括两个示例
示例 展示如何声明实例化和调用委托
示例 展示如何组合两个委托
此外还讨论以下主题
委托和事件
委托与接口
示例
下面的示例阐释声明实例化和使用委托BookDB 类封装一个书店数据库它维护一个书籍数据库它公开 ProcessPaperbackBooks 方法该方法在数据库中查找所有平装书并为每本书调用一个委托所使用的 delegate 类型称为 ProcessBookDelegateTest 类使用该类输出平装书的书名和平均价格
委托的使用促进了书店数据库和客户代码之间功能的良好分隔客户代码不知道书籍的存储方式和书店代码查找平装书的方式书店代码也不知道找到平装书后将对平装书进行什么处理
// bookstorecs
using System;
// A set of classes for handling a bookstore:
namespace Bookstore
{
using SystemCollections;
// 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)
{
listAdd(new Book(title author price paperBack))
}
// Call a passedin delegate on each paperback book to process it:
public void ProcessPaperbackBooks(ProcessBookDelegate processBook)
{
foreach (Book b in list)
{
if (bPaperback)
// 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 = ;
decimal priceBooks = m;
internal void AddBookToTotal(Book book)
{
countBooks += ;
priceBooks += bookPrice;
}
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)
{
ConsoleWriteLine( {} bTitle)
}
// Execution starts here
static void Main()
{
BookDB bookDB = new BookDB()
// Initialize the database with some books:
AddBooks(bookDB)
// Print all the titles of paperbacks:
ConsoleWriteLine(Paperback Book Titles:)
// Create a new delegate object associated with the static
// method TestPrintTitle:
bookDBProcessPaperbackBooks(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:
bookDBProcessPaperbackBooks(new ProcessBookDelegate(totallerAddBookToTotal))
ConsoleWriteLine(Average Paperback Book Price: ${:###}
totallerAveragePrice())
}
// Initialize the book database with some test books:
static void AddBooks(BookDB bookDB)
{
bookDBAddBook(The C Programming Language
Brian W Kernighan and Dennis M Ritchie m true)
bookDBAddBook(The Unicode Standard
The Unicode Consortium m true)
bookDBAddBook(The MSDOS Encyclopedia
Ray Duncan m false)
bookDBAddBook(Dogberts Clues for the Clueless
Scott Adams m true)
}
}
}
输出
Paperback Book Titles:
The C Programming Language
The Unicode Standard
Dogberts Clues for the Clueless
Average Paperback Book Price: $
代码讨论
声明委托 以下语句
public delegate void ProcessBookDelegate(Book book)
声明一个新的委托类型每个委托类型都描述参数的数目和类型以及它可以封装的方法的返回值类型每当需要一组新的参数类型或新的返回值类型时都必须声明一个新的委托类型
实例化委托 声明了委托类型后必须创建委托对象并使之与特定方法关联与所有其他对象类似新的委托对象用 new 表达式创建但创建委托时传递给 new 表达式的参数很特殊它的编写类似于方法调用但没有方法的参数
下列语句
bookDBProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle))
创建与静态方法 TestPrintTitle 关联的新的委托对象下列语句
bookDBProcessPaperbackBooks(new
ProcessBookDelegate(totallerAddBookToTotal))
创建与对象 totaller 上的非静态方法 AddBookToTotal 关联的新的委托对象在两个例子中新的委托对象都立即传递给 ProcessPaperbackBooks 方法
请注意一旦创建了委托它所关联到的方法便永不改变委托对象不可改变
调用委托 创建委托对象后通常将委托对象传递给将调用该委托的其他代码通过委托对象的名称(后面跟着要传递给委托的参数括在括号内)调用委托对象下面是委托调用的示例
processBook(b)
示例
本示例演示组合委托委托对象的一个有用属性是它们可以+运算符来组合组合的委托依次调用组成它的两个委托只可组合相同类型的委托并且委托类型必须具有 void 返回值运算符可用来从组合的委托移除组件委托
// composecs
using System;
delegate void MyDelegate(string s)
class MyClass
{
public static void Hello(string s)
{
ConsoleWriteLine( Hello {}! s)
}
public static void Goodbye(string s)
{
ConsoleWriteLine( Goodbye {}! 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 are composed to form c
// which calls both methods in order:
c = a + b;
// Remove a from the composed delegate leaving d
// which calls only the method Goodbye:
d = c a;
ConsoleWriteLine(Invoking delegate a:)
a(A)
ConsoleWriteLine(Invoking delegate b:)
b(B)
ConsoleWriteLine(Invoking delegate c:)
c(C)
ConsoleWriteLine(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!
委托和事件
委托非常适合于用作事件(从一个组件就该组件中的更改通知侦听器)
委托与接口
委托和接口的类似之处是它们都允许分隔规范和实现多个独立的作者可以生成与一个接口规范兼容的多个实现类似地委托指定方法的签名多个作者可以编写与委托规范兼容的多个方法何时应使用接口而何时应使用委托呢?
委托在以下情况下很有用
调用单个方法
一个类可能希望有方法规范的多个实现
希望允许使用静态方法实现规范
希望类似事件的设计模式
调用方不需要知道或获得在其上定义方法的对象
实现的提供程序希望只对少数选择组件分发规范实现
需要方便的组合
接口在以下情况下很有用
规范定义将调用的一组相关方法
类通常只实现规范一次
接口的调用方希望转换为接口类型或从接口类型转换以获得其他接口或类