转: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#委托和事件
委托和事件都可以用来调用跟自己方法签名一样的方法,两者在使用中主要有以下区别: 委托和事件没有可比性,因为委托是类型,事件是对象: 委托可以在声明它的类外部进行调用,而事件只能在类的内部进行调用: 委 ...
随机推荐
- Comparable<T> 和 Comparator<T>
相同点: Comparable<T> 和 Comparator<T>都是接口 不同点: 两者声明的方法不同.前者是compareTo()方法,后者是compare()方法. C ...
- CentOS 6.9配置EPEL源
简介: EPEL是一个由特别兴趣小组创建.维护并管理的,针对 红帽企业版 Linux(RHEL)及其衍生发行版(比如 CentOS.Scientific Linux.Oracle Enterprise ...
- Linux下以特定用户运行命令
方法汇总: 1.su 2.sudo 3.runuser 比较常用的方式:su 示例:su - root -s /bin/sh -c "/usr/local/nginx/sbin/nginx& ...
- SyncThingWin -- Run syncthing as a windows service
SyncThingWin Auto restart and minor bug fixes bloones released this on 23 Dec 2014 There is now an a ...
- ORA-06502: PL/SQL: 数字或值错误 : 字符串缓冲区太小解决办法
1.今天写的存储过程在执行过程中,报如下错误. exec PRO_T_008pro_update_add_delete(17,1,1,1,1,45.0,54.0,45.0,45.0,45.0,54.0 ...
- JavaScript 新手的踩坑日记
引语 在1995年5月,Eich 大神在10天内就写出了第一个脚本语言的版本,JavaScript 的第一个代号是 Mocha,Marc Andreesen 起的这个名字.由于商标问题以及很多产品已经 ...
- xarmain使用Forms编译android工程出现support_r19.0.1.zip支持包错误
第一次使用xarain下载Forms程序,提示一下错误. C:\Program Files (x86)\MSBuild\Xamarin\Android\Xamarin.Android.Common.t ...
- TIF、JPG图片手动添加地理坐标的方法
题目:为TIF.JPG图片添加地理坐标/平面直角坐标. 图片来源:GOOGLE EARTH.(当然也可以是其他知道四角点坐标的图片) 截图工具:GEtscreen(此软件截图时可以自动生成图片四角点坐 ...
- C语言变量的类型和存储位置
. C语言变量主要分为全局变量.静态全局变量.局部变量.静态局部变量和寄存器变量.其中静态变量用static关键字进行修饰.程序所占用的内存可以分为以下几个部分: ()代码段-存放程序代码,只读的,不 ...
- ubuntu下mongodb启动脚本
run-mongodb.sh #!/bin/bash mongod --dbpath /usr/local/mongodb/data1 --logpath /usr/local/mongodb/log ...