转: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#委托和事件
委托和事件都可以用来调用跟自己方法签名一样的方法,两者在使用中主要有以下区别: 委托和事件没有可比性,因为委托是类型,事件是对象: 委托可以在声明它的类外部进行调用,而事件只能在类的内部进行调用: 委 ...
随机推荐
- MySQL从库com_insert无变化的原因
大家都知道com_insert等com_xxx参数可以用来监控数据库实例的访问量,也就是我们常说的QPS.并且基于MySQL的复制原理,所有主库执行的操作都会在从库重放一遍保证数据一致,那么主库的co ...
- STM32F4: GENERATING A SINE WAVE
http://amarkham.com/?p=49
- Linux下open与fopen的区别
int open(const char *path, int access,int mode) path 要打开的文件路径和名称 access 访问模式,宏定义和含义如下: ...
- Hello World on Impala
Cloudera Impala 官方教程 <Impala Tutorial>,解说了Impala一些基本操作,但操作步骤前后缺少连贯性,本文节W选<Impala Tutorial&g ...
- Android之ConnectivityManager
在android平台中ConnectivityManager主要负责查询网络连接状态以及在连接状态有变化的时候发出通知.其主要的功能职责如下: 1. 监视网络状态,包括(Wi-Fi.GPRS.UMT ...
- 【springMVC 后台跳转前台】1.使用ajax访问的后台,后台正常执行,返回数据,但是不能进入前台的ajax回调函数中 ----2.前后台都没有报错,不能进入ajax回调函数
问题1: 使用ajax访问的后台,后台正常执行,并且正常返回数据,但是不能进入前台的ajax回调函数中 问题展示: 问题解决: 最后发现是因为后台的方法并未加注解:@ResponseBody,导致方 ...
- mysql访问权限GRANT ALL PRIVILEGES ON,访问权限表
开启远程连接:2, 修改 Mysql-Server 用户配置mysql> USE mysql; -- 切换到 mysql DBDatabase changedmysql> SELECT U ...
- An easier way to debug windows services
Have you got tired of attaching the Visual Studio debugger to the service application? I got the sol ...
- 使用route add添加路由,使两个网卡同时访问内外网
route add命令格式:route [-f] [-p] [Command] [Destination] [mask Netmask] [Gateway] [metric Metric] [if I ...
- iOS开发-照片选择
本来想做个注册登录的表单的,想想还是先做个简单的头像选择,一般情况下不管是内部管理系统还是面向公众的互联网公司,注册登录是免不了的,用户头像上传是免不了的,尤其是企业用户,上传了自己的图片才感觉自己买 ...