delegate委托:
三个概念:
C# 中的委托类似于 C 或 C++ 中的函数指针。
委托是面向对象、类型安全的,并且是安全的。
委托和接口的类似之处是:它们都允许分隔规范和实现。
一个原则:
被托管函数的返回和参数必须和声明的托管相同;
基于这三个概念来看这个例子,这是微软的教程上讲的一个例子:
示例阐释声明、实例化和使用委托。BookDB 类封装一个书店数据库,它维护一个书籍数据库。它公开 ProcessPaperbackBooks 方法,该方法在数据库中查找所有平装书,并为每本书调用一个委托。所使用的 delegate 类型称为 ProcessBookDelegate。Test 类使用该类输出平装书的书名和平均价格。
委托的使用促进了书店数据库和客户代码之间功能的良好分隔。客户代码不知道书籍的存储方式和书店代码查找平装书的方式。书店代码也不知道找到平装书后将对平装书进行什么处理。
using System;
namespace Bookstore
{
using System.Collections;
//1 声明Book
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;
}
}
//2 托管声明:注意参数是Book,返回值void
public delegate void ProcessBookDelegate(Book book);
//3 书库
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));
}
public void ProcessPaperbackBooks(ProcessBookDelegate processBook)
{
foreach (Book b in list)
{
if (b.Paperback)
//12.processBook,他的调用和在运行是调用的就是PrintTitle()
processBook(b);
}
}
}
}
// 4 使用端
namespace BookTestClient
{
using Bookstore;
// Class to total and average prices of books:
class PriceTotaller
{
int countBooks = 0;
decimal priceBooks = 0.0m;
//internal 修饰符使类、接口或成员仅在当前包中可见。
//当前包之外的代码不能访问 internal 成员。
//在全局范围内,internal 修饰符与 public 修饰符相同
internal void AddBookToTotal(Book book)
{
countBooks += 1;
priceBooks += book.Price;
}
internal decimal AveragePrice()
{
return priceBooks / countBooks;
}
}
//5.测试
class Test
{
// 打印书名
static void PrintTitle(Book b)
{
Console.WriteLine(" {0}", b.Title);
}
// 6 开始
static void Main()
{
//构造书库,这不必细说,从BookDB可以看很明白
BookDB bookDB = new BookDB();
// 7初始化书库,函数在往下找8
AddBooks(bookDB);
// 10 打印所有平装书
Console.WriteLine("Paperback Book Titles:");
// 11.关键点到了,新建了一个托管对象,参数是Test.PrintTitle
// 什么意思呢?先看PrintTitle(),他的参数和返回值和声明的托管
//要求的一致,我得理解是把这个函数的引用或者函数指针封装之后,当作参数
//交给函数ProcessPaperbackBooks 向上到12
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));
//13 同样的函数只要和委托在返回和参数上相同,就可以用了!
PriceTotaller totaller = new PriceTotaller();
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal));
Console.WriteLine("Average Paperback Book Price: ${0:#.##}",
totaller.AveragePrice());
}
// 8 把书存到里面
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);
}
}
}
另外:委托对象的一个有用属性是,它们可以“+”运算符来组合。组合的委托可调用组成它的那两个委托。只有相同类型的委托才可以组合。
“-”运算符可用来从组合的委托移除组件委托。
// 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, are 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");
}
}