C#的Delegate委托可以把方法当做参数传递,方便我们回调方法,它的用处很多,为我们编程提供了很大的便利。而Event又是委托的特殊形式。
委托Delegate
public delegate void GreetingDelegate(string name);
delegate 是委托的关键字,GreetingDelegate是一个委托类名,在编译后,它会由编译器生成委托类
public sealed class GreetingDelegate:Syste.MulticastDelegate{
public GreetingDelegate(object @object, IntPtr method);
public virtual IAsyncResult BeginInvoke(string name, AsyncCallback callback, object @object);
public virtual void EndInvoke(IAsyncResult result);
public virtual void Invoke(string name);
}
(BeginInvoke和EndInvoke,是对委托异步回调的支持,关于异步回调,会在后面文章中涉及。)
方法定义,此方法只要传入参数和返回类型与委托定义的一样,就可以赋值给委托
public void EnglishGreeting(string name)
{
}
首先委托类可以像参数一样传递在方法,属性中,如下:
public void GreetPeople(string name, GreetingDelegate MakeGreeting)
{
MakeGreeting(name);
}
GreetPeople("Jimmy Zhang", EnglishGreeting); //这代码看着有点奇怪吧?直接把方法作为了参数传递
委托类是需要实例化的,其实上面的方法内部已经帮你实例化了。它的实例化过程如下:
GreetingDelegate delegate1;
delegate1 = EnglishGreeting;// 以赋值的方式实例化
GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);//以构造方法方式实例化
委托可以通过赋值方式实例化,也可以通过构造方式实例化,GreetPeople("Jimmy Zhang", EnglishGreeting)这里是通过赋值的方式实例化的
可以将多个方法赋给同一个委托,或者叫将多个方法绑定到同一个委托,当调用这个委托的时候,将依次调用其所绑定的方法。
delegate1 = EnglishGreeting; // 先给委托类型的变量赋值
delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法
delegate1 -= ChineseGreeting; //当然也可以去掉一个委托方法
Event 事件
public event GreetingDelegate MakeGreet;
定义中多了event关键字,GreetingDelegate是上面定义的委托名。它封装了委托类型的变量,使得在类的内部,不管你声明它是public还是protected,它总是private的。在类的内部,可以直接调用它。在类的外部,只能使用+=或-=为它注册事件的执行方法,不能调用它去执行,这个是类的封装,只能由内部去触发事件(但是delegate是可以在外部直接调用,不需要封装方法的)。
委托事件
按照.Net Framework的编码规范:委托事件的名称都应该以EventHandler结束。 委托事件的原型定义:有一个void返回值,并接受两个输入参数:一个Object 类型,一个 EventArgs类型(或继承自EventArgs)。 事件的命名为 委托去掉 EventHandler之后剩余的部分。 继承自EventArgs的类型应该以EventArgs结尾。
定义委托事件的代码如下:
public delegate void ShowMessageEventHandler(Object sender, MessageEventArgs e); sender参数可以传递是谁触发的,e参数可以自定义一些需要的信息
public event ShowMessageEventHandler ShowMessage; // 命名规范,一般以EventHandler结尾。
定义eventargs
public class MessageEventArgs : EventArgs
{
//内部可以定义自己想要的信息
}
//注册的方法,方法签名必须与定义的委托的方法的签名相同
void EventTest_ShowMessage(object sender, MessageEventArgs e)
{
Console.WriteLine("you have triggered ShowMessage event");
}
注册事件
ShowMessage += EventTest_ShowMessage;
注册的时间可以是new ShowMessageEventHandler(EventTest_ShowMessage),也可以是+=,也可以用-=移出注册的事件
触发事件
ShowMessage(this, new MessageEventArgs());//如果在定义此事件的内部类,可以用这种方式直接触发。
在类的外部想调用ShowMessage,需要封装成一个方法,虽然是public的,但因为有event关键字,所以是无法编译通过的。如
public void TriggerEvent()
{
if (ShowMessage != null) {
ShowMessage(this, new MessageEventArgs("event is from trigger"));
}
}
经过测试,对于event事件,定义时也可以是如下的这种形式
public delegate void NoParamEventHandler();
public event NoParamEventHandler NoParam;
public void NoParamMethod()
{
Console.WriteLine("you have triggered NoParam event");
}
注册
NoParam += NoParamMethod;
触发
NoParam();
所以对于event与delegate的区别,<pre name="code" class="html">我的理解是虽然可以类似delegate一样使用,但事件是具有特殊含义的,专门用来处理事件模型,但是最好还是按照clr的编码规范来定义和使用,这样方便大家对代码的阅读和理解。
顺便讲一下Func和Action,这2个是已经为我们定义好了的委托,方便让我们使用,我们不需要自己定义委托,直接可以用Func<T,TResult>和Action<T>,把签名一样的方法赋值
给它们。