转:C#委托与事件
委托与事件
Ganesh Nataraj最近写了一篇解释委托与事件的文章,在坊间流传较广,今天翻译成中文与大家共享,如有不妥之处,欢迎留言讨论。
C#中的委托类似于C或C++中的函数指针。程序设计人员可以使用委托将方法的引用压缩到委托对象中,委托对象能被传递给调用该方法引用的代码而无须知道哪个方法将在编译时被调用。与C或C++中的指针不同的是,委托是面向对象的、类型安全的、受保护的。
委托声明时定义一个返回压缩方法的类型,其中包含一组特定的描述和返回类型。对于静态方法而言,委托对象压缩其调用的方法。对于实例方法(instance methods)而言,委托对象将压缩一个实例和实例的一个方法。如果一个委托对象有一组适当的描述,可以调用带描述的委托。
委托有趣而实用的一个特征就是它不用知道也无需关心它引用对象的类,任何对象都可以,关键的是方法的描述类型和引用类型要与委托的匹配。这使委托特别适合一些匿名的请求。
注意:委托以调用方的安全许可身份运行,而不是以声明方的许可运行。
下面有两个委托的示例:
例1向大家说明如何声明、实例化和调用一个委托;
例2向大家说明如何联合两个委托。
例1
这个例子说明如何声明、实例化和使用委托。BookDB类压缩了一个包含各种书籍的书店数据库,它对外暴露PRocessPaperbackBooks方法,用以查找数据库中所有平装本的书籍并调用委托,使用委托来调用ProcessBookDelegate。Test类使用这个类来打印处平装本书籍的标题和平均价格。
委托的使用促进了书店数据库与客户端代码之间功能性的良好分离。客户端代码不用知晓书是如何存的如何找到平装本,而书店的代码不用知道查找到该平装书并提供给客户端后将会被如何处理。代码如下(为了部影响理解,代码保持原样):
1// bookstore.cs
2using System;
3// A set of classes for handling a bookstore:
4namespace Bookstore
5{
6 using System.Collections;
7 // Describes a book in the book list:
8 public struct Book
9 {
10 public string Title; // Title of the book.
11 public string Author; // Author of the book.
12 public decimal Price; // Price of the book.
13 public bool Paperback; // Is it paperback?
14 public Book(string title, string author, decimal price, bool paperBack)
15 {
16 Title = title;
17 Author = author;
18 Price = price;
19 Paperback = paperBack;
20 }
21 }
22
23 // Declare a delegate type for processing a book:
24 public delegate void ProcessBookDelegate(Book book);
25
26 // Maintains a book database.
27 public class BookDB
28 {
29 // List of all books in the database:
30 ArrayList list = new ArrayList();
31
32 // Add a book to the database:
33 public void AddBook(string title, string author, decimal price, bool paperBack)
34 {
35 list.Add(new Book(title, author, price, paperBack));
36 }
37
38 // Call a passed-in delegate on each paperback book to process it:
39 public void ProcessPaperbackBooks(ProcessBookDelegate processBook)
40 {
41 foreach (Book b in list)
42 {
43 if (b.Paperback)
44
45 // Calling the delegate:
46 processBook(b);
47 }
48 }
49 }
50}
51// Using the Bookstore classes:
52namespace BookTestClient
53{
54 using Bookstore;
55
56 // Class to total and average prices of books:
57 class PriceTotaller
58 {
59 int countBooks = 0;
60 decimal priceBooks = 0.0m;
61 internal void AddBookToTotal(Book book)
62 {
63 countBooks += 1;
64 priceBooks += book.Price;
65 }
66 internal decimal AveragePrice()
67 {
68 return priceBooks / countBooks;
69 }
70 }
71 // Class to test the book database:
72 class Test
73 {
74 // Print the title of the book.
75 static void PrintTitle(Book b)
76 {
77 Console.WriteLine(" {0}", b.Title);
78 }
79 // Execution starts here.
80 static void Main()
81 {
82 BookDB bookDB = new BookDB();
83 // Initialize the database with some books:
84 AddBooks(bookDB);
85 // Print all the titles of paperbacks:
86 Console.WriteLine("Paperback Book Titles:");
87 // Create a new delegate object associated with the static
88 // method Test.PrintTitle:
89 bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));
90 // Get the average price of a paperback by using
91 // a PriceTotaller object:
92 PriceTotaller totaller = new PriceTotaller();
93 // Create a new delegate object associated with the nonstatic
94 // method AddBookToTotal on the object totaller:
95 bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal));
96 Console.WriteLine("Average Paperback Book Price: ${0:#.##}",
97 totaller.AveragePrice());
98 }
99 // Initialize the book database with some test books:
100 static void AddBooks(BookDB bookDB)
101 {
102 bookDB.AddBook("The C Programming Language",
103 "Brian W. Kernighan and Dennis M. Ritchie", 19.95m, true);
104 bookDB.AddBook("The Unicode Standard 2.0",
105 "The Unicode Consortium", 39.95m, true);
106 bookDB.AddBook("The MS-DOS Encyclopedia",
107 "Ray Duncan", 129.95m, false);
108 bookDB.AddBook("Dogbert's Clues for the Clueless",
109 "Scott Adams", 12.00m, true);
110 }
111 }
112}
113
输出:
平装书的标题:
The C Programming Language
The Unicode Standard 2.0
Dogbert's Clues for the Clueless
平均价格: $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异步调用。
例2(示范如何联合两个委托)
这个例子示范了委托的构成,委托对象的一个有用属性是他们可以使用”+”运算符来进行联合,联合委托调用组成它的两个委托,只有类型相同的委托才可以联合。”-”操作符用于从联合委托中移除一个委托。示例代码如下:
1// compose.cs
2using System;
3delegate void MyDelegate(string s);
4class MyClass
5{
6 public static void Hello(string s)
7 {
8 Console.WriteLine(" Hello, {0}!", s);
9 }
10 public static void Goodbye(string s)
11 {
12 Console.WriteLine(" Goodbye, {0}!", s);
13 }
14 public static void Main()
15 {
16 MyDelegate a, b, c, d;
17 // Create the delegate object a that references
18 // the method Hello:
19 a = new MyDelegate(Hello);
20 // Create the delegate object b that references
21 // the method Goodbye:
22 b = new MyDelegate(Goodbye);
23 // The two delegates, a and b, are composed to form c:
24 c = a + b;
25 // Remove a from the composed delegate, leaving d,
26 // which calls only the method Goodbye:
27 d = c - a;
28 Console.WriteLine("Invoking delegate a:");
29 a("A");
30 Console.WriteLine("Invoking delegate b:");
31 b("B");
32 Console.WriteLine("Invoking delegate c:");
33 c("C");
34 Console.WriteLine("Invoking delegate d:");
35 d("D");
36 }
37}
38
输出:
调用委托 a:
Hello, A!
调用委托b:
Goodbye, B!
调用委托c:
Hello, C!
Goodbye, C!
调用委托d:
Goodbye, D!
委托与事件
对于给组件的“听众”来通知该组件的发生的事件而言,使用委托特别适合。
委托 VS. 接口
委托与接口在都能促成规范与执行的分离,有相似之处。那声明时候使用委托声明时候使用接口呢?大体依据以下原则:
如下情况宜使用委托:
只调用单个方法时.
当一个类需要方法说明的多重执行时.
期望使用静态方法执行规范时.
期望得到一个类似事件的模式时.
调用者无需知道无需获取定义方法的对象时
只想给少数既定组件分发执行规范时.
想要简单的组成结构时.
如下情况宜使用接口:
当规范定义了一组需要调用的相关方法时.
一个类仅代表性地执行一次规范时.
接口的调用者想映射接口类型以获取其他类或接口时
转:C#委托与事件的更多相关文章
- .NET面试题系列[7] - 委托与事件
委托和事件 委托在C#中具有无比重要的地位. C#中的委托可以说俯拾即是,从LINQ中的lambda表达式到(包括但不限于)winform,wpf中的各种事件都有着委托的身影.C#中如果没有了事件,那 ...
- .NET基础拾遗(4)委托、事件、反射与特性
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...
- [转载]C#深入分析委托与事件
原文出处: 作者:风尘浪子 原文链接:http://www.cnblogs.com/leslies2/archive/2012/03/22/2389318.html 同类链接:http://www.c ...
- [转载]C#委托和事件(Delegate、Event、EventHandler、EventArgs)
原文链接:http://blog.csdn.net/zwj7612356/article/details/8272520 14.1.委托 当要把方法作为实参传送给其他方法的形参时,形参需要使用委托.委 ...
- C#委托与事件
一.在控制台下使用委托和事件 我们都知道,C#中有"接口"这个概念,所谓的"接口"就是定义一套标准,然后由实现类来具体实现其中的方法,所以说"接口,是 ...
- C#委托与事件的简单使用
前言:上一篇博文从原理和定义的角度介绍了C#的委托和事件.本文通过一个简单的小故事,来说明C#委托与事件的使用方法及其方便之处. 在阅读本文之前,需要你对委托和事件的基本概念有所了解.如果你是初次接触 ...
- C#之委托与事件
委托与事件 废话一堆:网上关于委托.事件的文章有很多,一千个哈姆雷特就有一千个莎士比亚,以下内容均是本人个人见解. 1. 委托 1.1 委托的使用 这一小章来学习一下怎么简单的使用委托,了解一些基本的 ...
- [ASP.NET MVC 大牛之路]02 - C#高级知识点概要(1) - 委托和事件
在ASP.NET MVC 小牛之路系列中,前面用了一篇文章提了一下C#的一些知识点.照此,ASP.NET MVC 大牛之路系列也先给大家普及一下C#.NET中的高级知识点.每个知识点不太会过于详细,但 ...
- .NET委托和事件
.net学习之委托和事件 1.什么是委托 通俗的说:委托就是一个能够存储符合某种格式(方法签名)的方法的指针的容器 上传图片: 2.委托语法 准备一个方法:string Hello(string ...
- C#委托和事件
委托和事件都可以用来调用跟自己方法签名一样的方法,两者在使用中主要有以下区别: 委托和事件没有可比性,因为委托是类型,事件是对象: 委托可以在声明它的类外部进行调用,而事件只能在类的内部进行调用: 委 ...
随机推荐
- Linux下分割、合并文件——dd和cat
功能说明:读取,转换并输出数据. 语 法:dd [bs=<字节数>][cbs=<字节数>][conv=<关键字>][count=<区块数>][ibs=& ...
- 利用.bat文件快速设置IE代理与清除IE代理
http://www.duoluodeyu.com/2009/17.html 设置IE代理.bat文件原文:将下面红色文字复制保存为.bat文件即可. 复制后将蓝色字体部分改成你要设置的代理服务器地址 ...
- QN-H618 遥控器复制再生仪(拷贝机)
针对现在市场上日益更新的遥控器种类,本公司经过长时间的研究,推出新一代拷贝机,本产品有以下特点: 1. 众多车库门遥控分析信息被集成在一台机器内,只要一种遥控器,就可以复制众多品牌的车库遥控.免去积压 ...
- +5v to +13v Converter
http://www.romanblack.com/smps/conv.htm What is it? This is a simple smps voltage converter, it mak ...
- 算法学习 - 平衡二叉查找树实现(AVL树)
平衡二叉查找树 平衡二叉查找树是非常早出现的平衡树,由于全部子树的高度差不超过1,所以操作平均为O(logN). 平衡二叉查找树和BS树非常像,插入和删除操作也基本一样.可是每一个节点多了一个高度的信 ...
- Material Design(原质化设计)视觉设计语言规范 踏得网镜像
Android 5.0 Lollipop(棒棒糖,也就是之前的代称Android L)全面实践了谷歌最新研发的 Material Design 设计语言规范,只是该设计规范并不是仅针对移动平台. 我们 ...
- C#位运算符的基本用法
位运算符包括:| 按位或 OR,& 按位与 AND,^ 按位异或 XOR,~ 取反 NOT,<< 左移 Left Shift,>> 右移 Right Shift,等等. ...
- JAVA基础知识要点
MQ.dubbo.SpringCloud 1) 集合框架 2)线程 3)IO流 4)类和对象生命周期 5)JAVA的反射机制 6) JVM 7)数据结构和常用算法 8)设计模式 9)网络编程
- jdbc连接rac的oracle数据库
jdbc连接rac的oracle数据库需要配置所有racIP,如下: DB1 =(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(H ...
- TR069协议小结
也称为CWMP,是在Internet网上通过wan口控制通信终端设备的协议.其协议流程如下图所示: 具体网上有很多资料.其主要的两个内容是:HTTP Client模型.DATA模型. ...