Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2914087
  • 博文数量: 199
  • 博客积分: 1400
  • 博客等级: 上尉
  • 技术积分: 4126
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-06 19:06
个人简介

半个PostgreSQL DBA,热衷于数据库相关的技术。我的ppt分享https://pan.baidu.com/s/1eRQsdAa https://github.com/chenhuajun https://chenhuajun.github.io

文章分类

全部博文(199)

文章存档

2020年(5)

2019年(1)

2018年(12)

2017年(23)

2016年(43)

2015年(51)

2014年(27)

2013年(21)

2011年(1)

2010年(4)

2009年(5)

2008年(6)

分类: C#/.net

2014-06-14 00:22:09

1. 委托
1.1 概述

委托相当于C语言的函数指针,但与函数指针不同的是委托是强类型的,使用起来更安全也更方便。而且委托还支持多路广播和异步调用。看看下面这个最简单的委托的例子
Code:

点击(此处)折叠或打开

  1. namespace Sample
  2. {
  3.     public delegate void CountChangedHandler(int count);

  4.     public class Counter
  5.     {
  6.         private int count = 0;
  7.         public CountChangedHandler CountChangedHandlers;

  8.         public void add()
  9.         {
  10.             count++;
  11.             if (CountChangedHandlers != null)
  12.                 CountChangedHandlers(count);
  13.         }
  14.     }

  15.     class Program
  16.     {
  17.         static void PrintHandler(int count)
  18.         {
  19.             System.Console.WriteLine("count={0}", count);
  20.         }

  21.         static void Main(string[] args)
  22.         {
  23.             Counter counter = new Counter();
  24.             counter.CountChangedHandlers = new CountChangedHandler(PrintHandler);

  25.             counter.add();
  26.             counter.add();
  27.         }
  28.     }
  29. }

Result:
count=1
count=2

每次add()方法被调用时,触法委托被调用。

1.2 委托对象的初始化
委托的对象可以用静态函数也可以用实例函数初始化。比如
  1. counter.CountChangedHandlers = new CountChangedHandler(PrintHandler);
  1. counter.CountChangedHandlers = new CountChangedHandler(objA.PrintHandler);


也可以隐式创建委托对象

  1. counter.CountChangedHandlers = PrintHandler;
  2. counter.CountChangedHandlers = objA.PrintHandler;

1.3
如果回调函数只在一处用到,专门定义一个函数岂不浪费。于是可以用下面的匿名委托。
  1. counter.CountChangedHandlers = delegate(int count)
  2.             {
  3.                 System.Console.WriteLine("count={0}", count);
  4.             };
匿名委托有个很大特点,就是可以访问所在方法的局部变量。
  1. int k = 0;
  2.             counter.CountChangedHandlers = delegate(int count)
  3.             {
  4.                 k++;
  5.                 System.Console.WriteLine("count={0}", count);
  6.             };
如果只看到表象一定迷惑于局部变量k的生命周期,担心匿名委托被调用时可能已经出了声明匿名委托的方法,k会消失。但通过ildasm可以看到,这种情况下,编译器会自动生成一个类,把被匿名委托访问的局部变量(k)变为这个类的实例变量,所有访问局部变量的匿名委托变成这个类的实例方法(
b__0))。也就是说此时k已经不是局部变量而是实例变量了。


如果匿名委托没有访问所在函数的局部变量,匿名委托会被自动生成为一个静态的函数(
b__0)。



Lambda可以认为是一种特殊语法形式的匿名委托
  1. counter.CountChangedHandlers = i => System.Console.WriteLine("count={0}", i);

1.4 多路广播
委托可以支持多路广播,也就是可以使用“+=”操作符注册多个回调函数。委托可以支持多路广播,也就是可以使用“+=”操作符注册多个回调函数。使用“+=”或“-=”时不用担心委托变量为null的情况会出现空指针异常。
Code:
  1. static void Main(string[] args)
  2.         {
  3.             Counter counter = new Counter();
  4.             counter.CountChangedHandlers += PrintHandler;//这里也可使用“=”
  5.             counter.CountChangedHandlers += PrintHandler;

  6.             counter.add();
  7.             counter.add();
  8.         }
Result:
count=1
count=1
count=2
count=2
上面把同一个函数注册了2次,所以也就会被调用2次。


使用“-=”操作符可以删除已注册的回调函数。
Code:

  1. static void Main(string[] args)
  2.         {
  3.             Counter counter = new Counter();

  4.             CountChangedHandler h = new CountChangedHandler(PrintHandler);
  5.             counter.CountChangedHandlers += h;
  6.             counter.CountChangedHandlers += h;
  7.             counter.CountChangedHandlers -= h;

  8.             counter.add();
  9.             counter.add();
  10.         }
Result:
count=1
count=2

上面注册了2次,删除了1次,所以最后被调用了1次。

1.5 异步调用委托
委托还可以被异步调用
Code:
  1. public void add()
  2.         {
  3.             count++;
  4.             if (CountChangedHandlers != null)
  5.             {
  6.                 IAsyncResult iAsyncRslt = CountChangedHandlers.BeginInvoke(count, null, null);
  7.                 System.Console.WriteLine("delegate call begin");
  8.                 CountChangedHandlers.EndInvoke(iAsyncRslt);
  9.                 System.Console.WriteLine("delegate call end");
  10.             }
  11.         }
Result:
delegate call begin
count=1
delegate call end
delegate call begin
count=2
delegate call end

1.6 委托的实现
通过ildasm查看由委托生成的代码,可以发现编译后声明的委托类型变成了一个继承自MulticastDelegate的类,并提供了几个调用委托的方法。




2. 事件
2.1 概述

在常用的事件监听应用场景下,被监听的对象只需要提供监听回调函数的注册和删除方法。而委托对象提供的功能太多,不宜公开,应该通过private修饰符隐藏起来。
比如像下面这样。
Code(修改前):
  1. public CountChangedHandler CountChangedHandlers;
Code(修改后):
  1. private CountChangedHandler CountChangedHandlers;

  2.         public void RegisterCountChangedHandler(CountChangedHandler handler)
  3.         {
  4.             CountChangedHandlers += handler;
  5.         }

  6.         public void UnRegisterCountChangedHandler(CountChangedHandler handler)
  7.         {
  8.             CountChangedHandlers -= handler;
  9.         }

然而如果在每个事件监听应用场景下都这么写不免繁琐,event就是解决这个问题的好办法。只要在原来声明委托对象的地方使用event关键字加以修饰,编译器就会自动做类似上面的事。被event关键字修饰后,在Counter对象外部只能通过"+="和"-="对事件注册或者删除监听回调函数,在Counter对象内部仍然可以像原来一样使用委托对象。
使用event后的完整代码示例如下:
Code:

点击(此处)折叠或打开

  1. namespace Sample
  2. {
  3.     public delegate void CountChangedHandler(int count);

  4.     public class Counter
  5.     {
  6.         private int count = 0;
  7.         public event CountChangedHandler CountChangedHandlers;

  8.         public void add()
  9.         {
  10.             count++;
  11.             if (CountChangedHandlers != null)
  12.                 CountChangedHandlers(count);
  13.         }
  14.     }

  15.     class Program
  16.     {
  17.         static void PrintHandler(int count)
  18.         {
  19.             System.Console.WriteLine("count={0}", count);
  20.         }

  21.         static void Main(string[] args)
  22.         {
  23.             Counter counter = new Counter();
  24.             counter.CountChangedHandlers += new CountChangedHandler(PrintHandler);

  25.             counter.add();
  26.             counter.add();
  27.         }
  28.     }
  29. }


2.2 事件的实现

通过ildasm查看编译后的代码,可以更清晰发现加与不加event的区别。加了event关键字后,委托对象变成了private的,并且自动生成了一个和委托对象同名的事件。
没有event关键字时:
public CountChangedHandler CountChangedHandlers;

 


有event关键字时:
public event CountChangedHandler CountChangedHandlers;

阅读(11126) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

skykiker2014-06-30 19:22:34

linustd:呵呵,竟然还有刚刚开始学习.net的。

作为一个过来人,由衷地告诉你,微软已经和苏联、毛主席一样走下神坛。

关键是,微软从来就是一个邪恶的公司,依靠技术营销模式套牢开发者和中小公司。

看看10年前学习.net和10年前学习Java, PHP, Linux的人,如今的下场, 就知道跟微软混的都是啥B了。

记住,跟着一个好老大,能受到老大的庇护,跟着一个坏老大,只能被这个老大剥削。

喷.NET就喷.NET,不必扯那么远。其实我也不喜欢.NET,也从没学过,一直在按Java的习惯用.NET,但最近遇到问题,需要搞清楚.NET里的一些概念。还有就是学什么东西往往不是自己能决定的,项目需要什么就要学什么。

回复 | 举报

linustd2014-06-28 19:49:53

呵呵,竟然还有刚刚开始学习.net的。

作为一个过来人,由衷地告诉你,微软已经和苏联、毛主席一样走下神坛。

关键是,微软从来就是一个邪恶的公司,依靠技术营销模式套牢开发者和中小公司。

看看10年前学习.net和10年前学习Java, PHP, Linux的人,如今的下场, 就知道跟微软混的都是啥B了。

记住,跟着一个好老大,能受到老大的庇护,跟着一个坏老大,只能被这个老大剥削。