委托与事件
    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#委托与事件的更多相关文章

  1. .NET面试题系列[7] - 委托与事件

    委托和事件 委托在C#中具有无比重要的地位. C#中的委托可以说俯拾即是,从LINQ中的lambda表达式到(包括但不限于)winform,wpf中的各种事件都有着委托的身影.C#中如果没有了事件,那 ...

  2. .NET基础拾遗(4)委托、事件、反射与特性

    Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...

  3. [转载]C#深入分析委托与事件

    原文出处: 作者:风尘浪子 原文链接:http://www.cnblogs.com/leslies2/archive/2012/03/22/2389318.html 同类链接:http://www.c ...

  4. [转载]C#委托和事件(Delegate、Event、EventHandler、EventArgs)

    原文链接:http://blog.csdn.net/zwj7612356/article/details/8272520 14.1.委托 当要把方法作为实参传送给其他方法的形参时,形参需要使用委托.委 ...

  5. C#委托与事件

    一.在控制台下使用委托和事件 我们都知道,C#中有"接口"这个概念,所谓的"接口"就是定义一套标准,然后由实现类来具体实现其中的方法,所以说"接口,是 ...

  6. C#委托与事件的简单使用

    前言:上一篇博文从原理和定义的角度介绍了C#的委托和事件.本文通过一个简单的小故事,来说明C#委托与事件的使用方法及其方便之处. 在阅读本文之前,需要你对委托和事件的基本概念有所了解.如果你是初次接触 ...

  7. C#之委托与事件

    委托与事件 废话一堆:网上关于委托.事件的文章有很多,一千个哈姆雷特就有一千个莎士比亚,以下内容均是本人个人见解. 1. 委托 1.1 委托的使用 这一小章来学习一下怎么简单的使用委托,了解一些基本的 ...

  8. [ASP.NET MVC 大牛之路]02 - C#高级知识点概要(1) - 委托和事件

    在ASP.NET MVC 小牛之路系列中,前面用了一篇文章提了一下C#的一些知识点.照此,ASP.NET MVC 大牛之路系列也先给大家普及一下C#.NET中的高级知识点.每个知识点不太会过于详细,但 ...

  9. .NET委托和事件

    .net学习之委托和事件   1.什么是委托 通俗的说:委托就是一个能够存储符合某种格式(方法签名)的方法的指针的容器 上传图片: 2.委托语法 准备一个方法:string Hello(string ...

  10. C#委托和事件

    委托和事件都可以用来调用跟自己方法签名一样的方法,两者在使用中主要有以下区别: 委托和事件没有可比性,因为委托是类型,事件是对象: 委托可以在声明它的类外部进行调用,而事件只能在类的内部进行调用: 委 ...

随机推荐

  1. spring---aop(3)---Spring AOP的拦截器链

    写在前面 时间断断续续,这次写一点关于spring aop拦截器链的记载.至于如何获取spring的拦截器,前一篇博客已经写的很清楚(spring---aop(2)---Spring AOP的JDK动 ...

  2. ubuntu 13.04 编译 安装 升级 gcc 4.9.0 address sanitizer

    @ 前记: 最近查一个线上项目的crash,review代码无果,crash几率低,不可在本地环境重现.之后在线上好几个服务器跑valgrind就不crash了.个人猜测可能是跑valgrind后性能 ...

  3. KVM资源划分分配技巧

    kvm有个叫做超分的概念,根据这个特性可以分配出超出物理机配置的数台虚拟机. 以下是自己总结的一些划分技巧: 一.最保守方法(性能最好) 根据物理机的资源,按虚拟机的数量叠加但不超过物理机的总和.不超 ...

  4. github个人博客绑定单独阿里域名指南

    详情:http://www.liu12fei08fei.top/ github个人博客绑定单独阿里域名指南 起源 禁止微信重拍版 给github pages绑定域名 获取github pages的ip ...

  5. Arcgis Runtime for andriod 100 加载geodatabase

    private void LoadMY(){ try { String mainGeodatabaseFilePath = YLPub.getMapData() + "/gismap/sl. ...

  6. SWT 全接触

    http://www.ibm.com/developerworks/cn/opensource/os-swt/index.html 1.SWT简介 SWT-"Standard Widget ...

  7. Ext.QuickTips.init()的使用

    在extJS的例子中,大部分都在程序第一行使用了如下语句:Ext.QuickTips.init();但是QuickTips的用处是什么呢?我们看一段最简单的代码: <html> <h ...

  8. 一种基于ES5的JavaScript继承

    关于JavaScript继承,方式非常多,包含compile-to-javascript的语言TypeScript, CoffeeScript以及站点MDN, GitHub, Modernizr各种p ...

  9. bochs和硬盘管理

    bochs和硬盘管理 实验一 目的:熟悉实验环境,认识Bochs虚拟机 内容: 1.下载并安装Bochs 2.3.7,官方网站 http://bochs.sourceforge.net/ 2.下载DO ...

  10. python笔记25-mock-server之moco

    前言 mock除了用在单元测试过程中,还有一个用途,当前端开发在开发页面的时候,需要服务端提供API接口 此时服务端没开发完成,或者说没搭建测试环境,这个时候前端开发会自己mock一个api服务端,自 ...