在.NET图像界面开发中最常见的一个事件大概就是按钮的clicked事件了,当点击图形界面的按钮时,就会调用一个与这个事件相关的方法对这个事件进行响应,做一些相关操作。但是事件并不局限于图像界面,也可能是满足程序的某个逻辑判断触发了事件。引发事件的对象叫做事件发送方,捕获事件并对其做出响应的对象叫做事件接收方。但是事件发送方并不知道哪个对象或者方法会处理它引发的事件,所以需要在事件发送方和事件接收方之间存在一媒介,明确某个对象或者某个类型的某个方法会对这个事件进行响应。.NET用委托作为事件发送方与事件接收方之间的媒介,委托只有一个签名,只要方法签名与委托签名匹配的方法,都可以声明自己对这个委托类型的事件的感兴趣,接收并处理它。
事件发送方的对象为了给事件接收对象传递一些额外信息,就需要写一个派生于System.EventArgs的类,封装一些数据。
public class DrawEventArgs:EventArgs
{
private double m_Size;
public DrawEventArgs(double size)
{
m_Size = size;
}
public double Size
{
get
{
return m_Size;
}
}
}
下面声明一委托,该委托有两个参数,一个是参数代表事件的发送方,另一个是参数是该事件封装的数据。
public delegate void ShapeSizeChanged(object sender,DrawEventArgs e);
ShapeSizeChanged的实例可以绑定到任何和它方法签名匹配的方法上。
下面是自定义事件委托,
public event ShapeSizeChanged SizeChanged;
下面是一个负责引发事件的类型代码,
public class DrawManager
{
public event ShapeSizeChanged SizeChanged;
protected void OnSizeChanged(DrawEventArgs e)
{
ShapeSizeChanged temp = SizeChanged;
//是否有委托与该事件关联
if (temp != null)
{
temp(this, e);
}
}
public void SizeChange(double size)
{
DrawEventArgs e = new DrawEventArgs(size);
OnSizeChanged(e);
}
}
而后定义两个监听事件的类型,
public class Square
{
public Square(DrawManager drawManager)
{
//drawManager.SizeChanged += DrawSquare;
//把事件关联到委托上
drawManager.SizeChanged += new ShapeSizeChanged(DrawSquare);
}
public void DrawSquare(object sender, DrawEventArgs e)
{
Console.WriteLine(string.Format("The Square'Length = {0}", e.Size));
}
public void Detach(DrawManager drawManager)
{
//drawManager.SizeChanged -= DrawSquare;
//解除事件和委托的关联
drawManager.SizeChanged -= new ShapeSizeChanged(DrawSquare);
}
}
public class Rectangle
{
public Rectangle(DrawManager drawManager)
{
drawManager.SizeChanged += DrawRectangle;
}
public void DrawRectangle(object sender, DrawEventArgs e)
{
Console.WriteLine(string.Format("The Rectangle'length={0} and width={1}.",e.Size*2,e.Size));
}
public void Detach(DrawManager drawManager)
{
drawManager.SizeChanged -= DrawRectangle;
}
}
测试代码,
class Program
{
static void Main(string[] args)
{
DrawManager drawManager = new DrawManager();
Rectangle rect = new Rectangle(drawManager);
Square square = new Square(drawManager);
//引发事件
drawManager.SizeChange(5);
//解除监听事件
square.Detach(drawManager);
drawManager.SizeChange(10);
Console.ReadLine();
}
}
/*运行结果
The Rectangle'length=10 and width=5.
The Square'Length = 5
The Rectangle'length=20 and width=10.
*/
.NET中的事件模式和观察者模式非常相似,也可谓是观察者模式在.NET下的进化版吧,下面用观察者模式实现上面功能以做对比,首先定义两个接口,IObserver和IObservable,如下
public interface IObserver
{
void Notify(DrawEventArgs e);
}
public interface IObservable
{
void Register(IObserver observer);
void UnRegister(IObserver observer);
}
下面是改写后的两个观察者类,
public class NewRectangle:IObserver
{
private ObserverManager m;
public NewRectangle(ObserverManager oManager)
{
m=oManager;
oManager.Register(this);
}
public void Notify(DrawEventArgs e)
{
Console.WriteLine(string.Format("The Rectangle'length={0} and width={1}.", e.Size * 2, e.Size));
}
public void Detach()
{
m.UnRegister(this);
}
}
public class NewSquare:IObserver
{
private ObserverManager m;
public NewSquare(ObserverManager oManager)
{
m=oManager;
oManager.Register(this);
}
public void Notify(DrawEventArgs e)
{
Console.WriteLine(string.Format("The Square'Length = {0}.", e.Size));
}
public void Detach()
{
m.UnRegister(this);
}
}
下面是负责通知观察者的类型,
public class ObserverManager:IObservable
{
protected ArrayList arrList;
public ObserverManager()
{
arrList = new ArrayList();
}
public void Register(IObserver observer)
{
arrList.Add(observer);
}
public void UnRegister(IObserver observer)
{
if (arrList.Contains(observer))
{
arrList.Remove(observer);
}
}
public void NotifyObservers(double size)
{
DrawEventArgs e = new DrawEventArgs(size);
foreach (IObserver observer in arrList)
{
observer.Notify(e);
}
}
public void SizeChanged(double size)
{
NotifyObservers(size);
}
}
下面是调用代码,
static void Main(string[] args)
{
ObserverManager oManager = new ObserverManager();
NewRectangle rect = new NewRectangle(oManager);
NewSquare square = new NewSquare(oManager);
oManager.SizeChanged(5);
square.Detach();
oManager.SizeChanged(10);
Console.ReadLine();
}
最好运行下代码,这样可以更easy的理解这两种模式微妙的差别了。
对事件来说,还可以显式的用add和remove编写事件访问器,事件访问器通常有编译器生成,所以可以显式的用事件访问器修改DrawManager类型,
public class DrawManager
{
private event ShapeSizeChanged m_SizeChanged;
private readonly object m_lock = new object();
public event ShapeSizeChanged SizeChanged
{
add
{
lock (m_lock)
{
m_SizeChanged += value;
}
}
remove
{
lock (m_lock)
{
m_SizeChanged -= value;
}
}
}
protected void OnSizeChanged(DrawEventArgs e)
{
ShapeSizeChanged temp = m_SizeChanged;
//是否有委托与该事件关联
if (temp != null)
{
temp(this, e);
}
}
public void SizeChange(double size)
{
DrawEventArgs e = new DrawEventArgs(size);
OnSizeChanged(e);
}
}