1. 委托
1.1 概述
委托相当于C语言的函数指针,但与函数指针不同的是委托是强类型的,使用起来更安全也更方便。而且委托还支持多路广播和异步调用。看看下面这个最简单的委托的例子
Code:
点击(此处)折叠或打开
- namespace Sample
- {
- public delegate void CountChangedHandler(int count);
-
- public class Counter
- {
- private int count = 0;
- public CountChangedHandler CountChangedHandlers;
-
- public void add()
- {
- count++;
- if (CountChangedHandlers != null)
- CountChangedHandlers(count);
- }
- }
-
- class Program
- {
- static void PrintHandler(int count)
- {
- System.Console.WriteLine("count={0}", count);
- }
-
- static void Main(string[] args)
- {
- Counter counter = new Counter();
- counter.CountChangedHandlers = new CountChangedHandler(PrintHandler);
-
- counter.add();
- counter.add();
- }
- }
- }
Result:
count=1
count=2
每次add()方法被调用时,触法委托被调用。
1.2 委托对象的初始化
委托的对象可以用静态函数也可以用实例函数初始化。比如
- counter.CountChangedHandlers = new CountChangedHandler(PrintHandler);
- counter.CountChangedHandlers = new CountChangedHandler(objA.PrintHandler);
也可以隐式创建委托对象
- counter.CountChangedHandlers = PrintHandler;
- counter.CountChangedHandlers = objA.PrintHandler;
1.3 匿 名 委 托
如果回调函数只在一处用到,专门定义一个函数岂不浪费。于是可以用下面的匿名委托。
- counter.CountChangedHandlers = delegate(int count)
- {
- System.Console.WriteLine("count={0}", count);
- };
- int k = 0;
- counter.CountChangedHandlers = delegate(int count)
- {
- k++;
- System.Console.WriteLine("count={0}", count);
- };
如果匿名委托没有访问所在函数的局部变量,匿名委托会被自动生成为一个静态的函数(b__0)。
Lambda可以认为是一种特殊语法形式的匿名委托
- counter.CountChangedHandlers = i => System.Console.WriteLine("count={0}", i);
1.4 多路广播
委托可以支持多路广播,也就是可以使用“+=”操作符注册多个回调函数。委托可以支持多路广播,也就是可以使用“+=”操作符注册多个回调函数。使用 “+=”或“-=”时不用担心委托变量为null的情况会出现空指针异常。
Code:
- static void Main(string[] args)
- {
- Counter counter = new Counter();
- counter.CountChangedHandlers += PrintHandler;//这里也可使用“=”
- counter.CountChangedHandlers += PrintHandler;
-
- counter.add();
- counter.add();
- }
count=1
count=1
count=2
count=2
上面把同一个函数注册了2次,所以也就会被调用2次。
使用“-=”操作符可以删除已注册的回调函数。
Code:
- static void Main(string[] args)
- {
- Counter counter = new Counter();
-
- CountChangedHandler h = new CountChangedHandler(PrintHandler);
- counter.CountChangedHandlers += h;
- counter.CountChangedHandlers += h;
- counter.CountChangedHandlers -= h;
-
- counter.add();
- counter.add();
- }
count=1
count=2
上面注册了2次,删除了1次,所以最后被调用了1次。
1.5 异步调用 委托
委托还可以被异步调用
Code:
- public void add()
- {
- count++;
- if (CountChangedHandlers != null)
- {
- IAsyncResult iAsyncRslt = CountChangedHandlers.BeginInvoke(count, null, null);
- System.Console.WriteLine("delegate call begin");
- CountChangedHandlers.EndInvoke(iAsyncRslt);
- System.Console.WriteLine("delegate call end");
- }
- }
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(修改前):
- public CountChangedHandler CountChangedHandlers;
- private CountChangedHandler CountChangedHandlers;
-
- public void RegisterCountChangedHandler(CountChangedHandler handler)
- {
- CountChangedHandlers += handler;
- }
-
- public void UnRegisterCountChangedHandler(CountChangedHandler handler)
- {
- CountChangedHandlers -= handler;
- }
然而如果在每个事件监听应用场景下都这么写不免繁琐,event就是解决这个问题的好办法。只要在原来声明委托对象的地方使用event关键字加以修饰,编译器就会自动做类似上面的事。被event关键字修饰后,在Counter对象外部只能通过"+="和"-="对事件注册或者删除监听回调函数,在Counter对象内部仍然可以像原来一样使用委托对象。
使用event后的完整代码示例如下:
Code:
点击(此处)折叠或打开
- namespace Sample
- {
- public delegate void CountChangedHandler(int count);
-
- public class Counter
- {
- private int count = 0;
- public event CountChangedHandler CountChangedHandlers;
-
- public void add()
- {
- count++;
- if (CountChangedHandlers != null)
- CountChangedHandlers(count);
- }
- }
-
- class Program
- {
- static void PrintHandler(int count)
- {
- System.Console.WriteLine("count={0}", count);
- }
-
- static void Main(string[] args)
- {
- Counter counter = new Counter();
- counter.CountChangedHandlers += new CountChangedHandler(PrintHandler);
-
- counter.add();
- counter.add();
- }
- }
- }
2.2 事件的实现
通过ildasm查看编译后的代码,可以更清晰发现加与不加event的区别。加了event关键字后,委托对象变成了private的,并且自动生成了一个和委托对象同名的事件。
没有event关键字时:
public CountChangedHandler CountChangedHandlers;

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