一、认识委托
可以把delegate理 解成C中的函数指针,它允许传递类A的方法M给类B的对象,使得类B的对象能够调用这个方法M,说白了就是可以把方法当作参数传递。
但 delegate和函数指针还是有些区别的,并且更具优势:
首先,函数指针只能指向静态函数,而delegate既可以引 用静态函数,又可以引用非静态成员函数。在引 用非静态成员函数时,delegate不但保存了对此函数入口指针的引用,而且还保存了调用此函数的类实例的引用。
其次,与函数指针相 比,delegate是面向对象、类型安全、可靠的受控(managed)对象。也就是说,runtime能够保证delegate指向一个有效的方法, 你无须担心delegate会指向无效地址或者越界地址。
二、定义一个委托(delegate)
1、声明一个delegate对象,它应当与你想要传递的方法具有相同的参数名和返回值类型
2、创建delegate对象,并将你想要传递的函数作为参数传入
3、在要实现异步调用的地方,通过上一步创建的对象来调用方法
三、定义一个事件
1、定义对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。
2、定义事件参数类,此类应当从System.EventArgs类派生,如果事件不带参数,这一步可以省略。
3、定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。
4、用event关键字定义事件对象,它同时也是一个delegate对象。
5、用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。
6、在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是可以是OnEventName
7、在适当的地方调用事件触发方法触发事件。
四、.NET Framework中标准委托与事件
.NETFramework中,标准的委托已经定义在命名空间中:
namespace System
{
public delegate void EventHandler(object sender, EventArgs e);
}
.NETFramework类库中所有事件均基于EventHandler。
.NETFramework中事件参数的定义必须继承自EventArgs:
public class CustomEventArgs:EventArgs
{
}
.NETFramework事件发布,可以有以下几种方式:
//自定义参数方式
public delegate void CustomEventHandler(object sender,CustomEventArgs e);
public event CustomEventHandler CustomEvent;
//标准参数方式
public event EventHandler CustomEvent;
//标准委托泛型方式
public event EventHandler<CustomEventArgs> CustomEvent;
综上所述,.NETFramework事件定义与发布一般遵循如下规范:
1、来源类定义:主要包含委托与事件引发定义
public class CustomEventSource
{
}
2、在类CustomEventSource中定义委托,例如:
public delegate void CustomEventHandler(object sender,CustomEventArgs e);
3、基于标准参数类(EventArgs),定义参数类,例如:
public class CustomEventArgs:EventArgs
{
}
4、在类CustomEventSource中定义事件,例如:
public event CustomEventHandler CustomEvent;
5、在类CustomEventSource中定义引发事件,例如:
public void RaiseCustomEvent()
{
//定义参数,或者直接new EventArgs()
CustomEventArgs e = new CustomEventArgs();
//委托类型事件响应
if(CustomEvent != null)
{
CustomEvent(this, e);
}
}
6、订阅类定义:包括事件处理方法定义、注册、取消注册,例如:
public class CustomEventListener
{
}
7 、在类CustomEventListener中添加事件处理方法,例如:
private void InputAction(object sender, EventArgs e)
{
Console.WriteLine("您按了{0}键", Console.ReadKey().KeyChar.ToString());
}
8、在类CustomEventListener中添加事件订阅,例如:
public void Subscrible(CustomEventSource customEventSource)
{
customEventSource.InputEvent += new CustomEventListener().InputAction;
}
9、在类CustomEventListener中添加事件取消订阅,例如:
public void UnSubscrible(CustomEventSource customEventSource)
{
customEventSource.InputEvent -= new CustomEventListener().InputAction;
}
10、定义事件使用,例如:
class Program
{
static void Main(string[] args)
{
//创建事件源对象
CustomEventSource evtSource = new CustomEventSource();
//创建监听对象
CustomEventListener evtListen = new CustomEventListener();
//订阅事件
Console.WriteLine("订阅事件");
evtListen.Subscrible(evtSource);
while (true)
{
evtSource.RaiseInputEvent();
}
}
}
五、一个完整的自定义事件
键盘输入或为单个字符或为字符串,自定义事件(ReadKeyEvent)将根据输入判定是字符还是字符串,完整代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyReadKey
{
public class ReadKeyEventSource
{
public event EventHandler<ReadKeyEventArgs> ReadKeyEvent;
public void RaiseReadKeyEvent()
{
while (true)
{
string str = Console.ReadLine();
ReadKeyEvent(this, new ReadKeyEventArgs(str));
}
}
}
public class ReadKeyEventListener
{
private void DoKeyEvent(object sender, ReadKeyEventArgs e)
{
Console.WriteLine(e.KeyboardValue.Length == 1 ? "您输入了字符:'{0}',ASCII码(" + ((int)e.KeyboardValue[0]).ToString() + ")" : "您输入了字符串:'{0}'", e.KeyboardValue);
}
public void Subscrible(ReadKeyEventSource readKeyEventSource)
{
readKeyEventSource.ReadKeyEvent += new ReadKeyEventListener().DoKeyEvent;
}
public void UnSubscrible(ReadKeyEventSource readKeyEventSource)
{
readKeyEventSource.ReadKeyEvent -= new ReadKeyEventListener().DoKeyEvent;
}
}
public class ReadKeyEventArgs : EventArgs
{
string _keyboardValue;
public ReadKeyEventArgs(string keyValue)
{
if (keyValue.Length == 1)
{
this._keyboardValue = keyValue[0].ToString();
}
else
{
this._keyboardValue = keyValue;
}
}
public string KeyboardValue
{
get
{
return _keyboardValue;
}
}
}
}
//调用程序
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyReadKey
{
class Program
{
static void Main(string[] args)
{
ReadKeyEventSource rkes = new ReadKeyEventSource();
ReadKeyEventListener rkel = new ReadKeyEventListener();
Console.WriteLine("请输入字符或字符串(输入后回车):");
rkel.Subscrible(rkes);
while (true)
{
rkes.RaiseReadKeyEvent();
}
}
}
}