C#中的委托和事件
一.理解什么事委托?
1.使用委托可以把一个方法以一个参数的形式代入到另一个方法里面(可以理解为指向一个函数的指针)。
例子:
using System;
using System.Collections.Generic;
using System.Text;
namespace Delegate
{
//定义委托,它定义了可以代表的方法的类型(有一个参数)
public delegate void GreetingDelegate(string name);
class Program
{
//定义英文问候的方法
private static void EnglishGreeting(string name)
{
Console.WriteLine("Good Morning, " + name);
}
//定义中文问候的方法
private static void ChineseGreeting(string name)
{
Console.WriteLine("早上好, " + name);
}
//注意此方法,它接受一个GreetingDelegate 委托类型的方法作为参数
private static void GreetPeople(string name, GreetingDelegate MakeGreeting)
{
MakeGreeting(name);
}
static void Main(string[] args)
{
GreetPeople("Liker", EnglishGreeting);
GreetPeople("李志中", ChineseGreeting);
Console.ReadLine();
}
}
}
总结:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If … Else(Switch)语句,同时使得程序具有更好的可扩展性。
2.将方法绑定到委托:可以将多个方法赋给同一个委托,或者将多个方法绑定到同一个委托,当调用这个委托的时候,将依次调用其所绑定的方法
依照上例,可以使用以下的方式进行方法的绑定
static void Main(string[] args)
{
GreetingDelegate delegate1; //定义一个委托
delegate1 = EnglishGreeting; // 先给委托类型的变量赋值
delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法
// 将先后调用 EnglishGreeting 与 ChineseGreeting 方法
GreetPeople("Liker", delegate1);
Console.ReadLine();
}
实际上,我们可以也可以绕过GreetPeople 方法,通过委托来直接调用EnglishGreeting 和ChineseGreeting:
static void Main(string[] args)
{
GreetingDelegate delegate1;
delegate1 = EnglishGreeting; // 先给委托类型的变量赋值
delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法
// 将先后调用 EnglishGreeting 与 ChineseGreeting 方法
delegate1 ("Liker");
Console.ReadLine();
}
第一次用的“=”,是赋值的语法;第二次,用的是“+=”,是绑定的语法。如果第一次就使用“+=”,将出现“使用了未赋值的局部变量”的编译错误。
当然,也可是使用下面的方法进行简化:(使用-=也可以取消绑定方法)
GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);
delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法
二、事件的由来
1.更好的封装性:
它封装了委托类型的变量,使得在类的内部,不管你声明它是public还是protected,它总是private 的。在类的外部,注册“+=”和注销“-=”的访问限定符与你在声明事件时使用的访问符相同。
我们改写GreetingManager 类:
public class GreetingManager
{
//这一次我们在这里声明一个事件
public event GreetingDelegate MakeGreet;
public void GreetPeople(string name)
{
MakeGreet(name);
}
}
很容易注意到:MakeGreet事件的声明与之前委托变量delegate1的声明唯一的区别是多了一个event关键字,所以,事件其实没什么不好理解的,声明一个事件不过类似于声明一个进行了封装的委托类型的变量而已。
现在修改mian方法做如下修改:
static void Main(string[] args)
{
GreetingManager gm = new GreetingManager();
gm.MakeGreet = EnglishGreeting; // 编译错误1 会得到编译错误:事件“Delegate.GreetingManager.MakeGreet”只能出现在“+=”或者“-=”的左边(从类型“Delegate.GreetingManager”中使用时除外)
gm.MakeGreet += ChineseGreeting;
gm.GreetPeople("Liker");
}
2.限制类型能力
使用事件不仅能获得比委托更好的封装性以外,还能限制含有事件的类型的能力,即事件应该由事件发布者触发,而不应该由事件的客户端(客户程序)来触发。
class Program
{
static void Main(string[] args)
{
Publishser pub = new Publishser(); //事件的发布者
Subscriber sub = new Subscriber(); //事件订阅者
pub.NumberChanged += new NumberChangedEventHandler(sub.OnNumberChanged);
pub.DoSomething(); // 应该通过DoSomething()来触发事件
pub.NumberChanged(100); // 但可以被这样直接调用,对委托变量的不恰当使用
}
}
// 定义委托
public delegate void NumberChangedEventHandler(int count);
// 定义事件发布者
public class Publishser
{
private int count;
public NumberChangedEventHandler NumberChanged; // 声明委托变量
//public event NumberChangedEventHandler NumberChanged; // 声明一个事件
public void DoSomething()
{
// 在这里完成一些工作 ...
if (NumberChanged != null)
{ // 触发事件
count++;
NumberChanged(count);
}
}
}
// 定义事件订阅者
public class Subscriber
{
public void OnNumberChanged(int count)
{
Console.WriteLine("Subscriber notified: count = {0}", count);
}
}
更多关于事件与委托知识点:http://kb.cnblogs.com/page/45756/