C#中的委托

N.1 前言

说来惭愧,我在使用C#一年之后,才知道C#的这个功能。虽然我很早就疑惑C#中有没有类似于C或C++中的函数指针,既然提到了函数指针,大家也自然知道了C#的委托与它的功能类似,即可以将函数作为一个参数来传递。C#中的事件即以委托作为基础,例如一个按钮,C#中定义了其鼠标点击事件,但点击后要做的动作,则由用户指派。

N.2 示例1

下面的示例阐释声明、实例化和使用委托。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;
}
}

// 声明委托方法
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)
// 调用委托方法
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);

// 打印所有书名
Console.WriteLine("Paperback Book Titles:");

// 创建与静态方法 Test.PrintTitle 关联的委托对象
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));

// 获取所有书的均价
PriceTotaller totaller = new PriceTotaller();

// 创建与非静态方法 AddBookToTotal 关联的委托对象
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);

    声明一个新的委托类型。每个委托类型都描述参数的数目和类型,以及它可以封装的方法的返回值类型。每当需要一组新的参数类型或新的返回值类型时,都必须声明一个新的委托类型。

  • 实例化委托 声明了委托类型后,必须创建委托对象并使之与特定方法关联。与所有其他对象类似,新的委托对象用 new 表达式创建。但是当创建委托时,传递给 new 表达式的参数很特殊:它的编写类似于方法调用,但没有方法的参数。

    下列语句:

    bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));

    创建与静态方法 Test.PrintTitle 关联的新的委托对象。下列语句:

    bookDB.ProcessPaperbackBooks(new 
    ProcessBookDelegate(totaller.AddBookToTotal));

    创建与对象 totaller 上的非静态方法 AddBookToTotal 关联的新的委托对象。在两个例子中,新的委托对象都立即传递给 ProcessPaperbackBooks 方法。

    请注意一旦创建了委托,它所关联到的方法便永不改变:委托对象不可改变。

  • 调用委托 创建委托对象后,通常将委托对象传递给将调用该委托的其他代码。通过委托对象的名称(后面跟着要传递给委托的参数,括在括号内)调用委托对象。下面是委托调用的示例:
    processBook(b);

    在此示例中,可以通过使用 BeginInvoke 和 EndInvoke 方法同步或异步调用委托。

N.3 示例2

本示例演示组合委托。委托对象的一个有用属性是,它们可以“+”运算符来组合。组合的委托可调用组成它的那两个委托。只有相同类型的委托才可以组合。

“-”运算符可用来从组合的委托移除组件委托。

// 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;

// 创建 Hello 方法的委托对象
a = new MyDelegate(Hello);

// 创建 Goodbye 方法的委托对象
b = new MyDelegate(Goodbye);

// 组合两个委托对象
c = a + b;

// 从组合委托中删除一个对象,只余 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!
 就我个人经验而言,  我经常在写一些公用的模块中加入一些委托方法, 这样可很方便地在定制模块中使用公用模块.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值