======================
一 不包含附加信息的事件
======================
1 定义事件委托
要使用事件首先要定义一个与事件相关的委托,因为事件发生后一定要有某个类中的方法来处理它,而委托正是由对象引用以及该对象的方法引用组成的,所以委托能够将事件绑定到用来处理它们的方法上。
★ 事件委托准则:
.NET Framework 指南指示用于事件的委托类型应采用两个参数:“对象源”参数(用于指示事件源)和特定于事件的参数(它封装有关事件的其他任何信息)。特定于事件的参数应从 EventArgs 类派生。对于不使用任何附加信息的事件,.NET Framework 提供了 EventHandler 类。
准则告诉我们用于事件的委托一定要有两个参数,第一个参数用于指示事件源,第二个参数用于指示特定事件,所谓特定事件指的是具有附加信息的事件和没有附加信息的事件,如果使用没有附加信息的事件,委托参数就使用内置的EventHandler类的形式。
★ EventHandler
EventHandler 是命名空间System中的一个预定义的委托,是专用于表示不生成数据的事件的事件处理程序方法。
语法
public delegate void EventHandler (
Object sender,
EventArgs e
);
参数
sender:事件源,类型为 Object,用于引发事件的实例。
e:不包含任何事件数据的 EventArgs。
解读:
语法给出了事件委托的基本形式,其中EventHandler是一个委托的名称,可以根据习惯任意更改,第一个参数中的Object表示数据类型,sender是使用该类型创建的实例名,第二个参数EventArgs是一个内置类,e 是该类的一个实例。
通过上面的学习,我们懂得了如何为事件定义一个委托,下面就定义这样一个委托。
//定义委托
public delegate void TestEventDelegate(object sender, System.EventArgs e);
2 声明事件
事件和方法一样具有签名,签名包括名称和参数列表。事件的签名使用委托的签名来表示。
声明事件需要使用 event 关键字。
语法
修饰符 event 委托名称 事件名称
public event TestEventDelegate TestEvent;
事件要放在引发事件的类中,定义一个事件引发类。
//定义事件引发类
public class EventSource
{
public event TestEventDelegate TestEvent;
private void RaiseTestEvent() { /* ... */ }
}
3 引发事件
若要引发事件,类可以调用委托,并传递所有与事件有关的参数。
private void RaiseTestEvent()
{
TestEventDelegate temp = TestEvent;
if (temp != null)
{
temp(this, new System.EventArgs());
}
}
4 订阅事件
要接收某个事件的类可以创建一个方法来接收该事件,然后向类事件自身添加该方法的一个委托。这个过程称为“订阅事件”。
首先,接收类必须具有与事件自身具有相同签名(如委托签名)的方法。然后,该方法(称为事件处理程序)可以采取适当的操作来响应该事件。
//定义事件接收类
public class EventReceiver
{
//定义事件处理方法
public void ReceiveTestEvent(object sender, System.EventArgs e)
{
System.Console.Write("Event received from ");
System.Console.WriteLine(sender.ToString());
}
}
若要订阅事件,接收器必须创建一个与事件具有相同类型的委托,并使用事件处理程序作为委托目标。然后,接收器必须使用加法赋值运算符 (+=) 将该委托添加到源对象的事件中。
//事件订阅方法
public void Subscribe(EventSource source)
{
TestEventDelegate temp = ReceiveTestEvent;
source.TestEvent += temp;
}
若要取消订阅事件,接收器可以使用减法赋值运算符 (-=) 从源对象的事件中移除事件处理程序的委托。例如:
//取消订阅方法
public void UnSubscribe(EventSource source)
{
TestEventDelegate temp = ReceiveTestEvent;
source.TestEvent -= temp;
}
5 整合
前面的代码不会运行,必需在入口方法中将他们整合。
static void Main()
{
EventReceiver receiver = new EventReceiver();
EventSource source = new EventSource();
receiver.Subscribe(source);
source.RaiseTestEvent();
}
完整代码
//定义委托
public delegate void TestEventDelegate(object sender, System.EventArgs e);
//事件引发类
public class EventSource
{
//声明事件
public event TestEventDelegate TestEvent;
//事件引发方法
private void RaiseTestEvent()
{
TestEventDelegate temp = TestEvent;
if (temp != null)
{
temp(this, new System.EventArgs());
}
}
static void Main()
{
EventReceiver receiver = new EventReceiver();
EventSource source = new EventSource();
receiver.Subscribe(source);
source.RaiseTestEvent();
}
}
//事件接收类
public class EventReceiver
{
//事件处理方法
public void ReceiveTestEvent(object sender, System.EventArgs e)
{
System.Console.WriteLine("Event received from");
System.Console.WriteLine(sender.ToString());
}
//事件订阅方法
public void Subscribe(EventSource source)
{
TestEventDelegate temp = ReceiveTestEvent;
source.TestEvent += temp;
}
}
事件使用过程小结:
1 定义委托
2 定义事件引发类
(1)定义事件
(2)定义引发方法,在这个方法中,将事件分配给一个委托实例,然后调用委托。
3 定义事件接收类
(1)定义事件处理方法
(2)定义事件订阅方法,在这个方法中,将事件处理方法分配给委托的一个实例,然后将委托绑定到事件。
=======================
二 包含附加信息的事件
=======================
如果使用包含附加信息的事件,委托的第二个参数应从 EventArgs 类派生。vs2005对EventArgs 类有如下说明:
★ EventArgs
EventArgs 是包含事件数据的类的基类。此类不包含事件数据,在事件引发时不向事件处理程序传递状态信息的事件会使用此类。如果事件处理程序需要状态信息,则应用程序必须从此类派生一个类来保存数据。
下面我们以台风登陆事件为例来说明,06年第8号超强台风“桑美”,中心风力17级,以每小时20公里的速度向我国东部沿海袭来,登陆地点是福建和浙江。
★ 定义信息保存类
台风登陆时需要说明登陆地点和风力,因此设计一个类用来保存这两项数据。
using System;
public class Typhoonclass : EventArgs
{
public string place;
public int level;
public Typhoonclass(string place, int level)
{
this.place = place;
this.level = level;
}
}
★ 事件引发类
public delegate void TyphoonDelegate
(object sender,Typhoonclass e);
public class EventSource
{
public event TyphoonDelegate TyphoonEvent;
public void Raise(string place, int level)
{
TyphoonDelegate temp = TyphoonEvent;
if (temp != null)
{
temp(this, new Typhoonclass(place,level));
}
}
}
★ 事件接收类
public class EventReceiver
{
public void EventHandling(object sender,Typhoonclass e)
{
Console.WriteLine("订阅方法被" +
sender.ToString() + "调用。");
if (e.level < 5)
Console.WriteLine("登陆" + e.level +
"的风力一般,要稍加防范");
else if (e.level < 10)
Console.WriteLine("登陆" + e.level +
"的风力较大,船只回港避风");
else
Console.WriteLine("登陆" + e.level +
"的风力很大,船只回港,人员疏散避风");
}
public void Subscribe(EventSource source)
{
TyphoonDelegate temp = EventHandling;
source.TyphoonEvent += temp;
}
}
public class Mainclass
{
public static void Main()
{
EventSource source = new EventSource();
EventReceiver receiver = new EventReceiver();
receiver.Subscribe(source);
source.Raise("浙江",7);
}
}
//=============完整代码=====================
using System;
public delegate void TyphoonDelegate(object sender, Typhoonclass e);
public class Typhoonclass : EventArgs
{
public string place;
public int level;
public Typhoonclass(string place, int level)
{
this.place = place;
this.level = level;
}
}
public class EventSource
{
public event TyphoonDelegate TyphoonEvent;
public void Raise(string place, int level)
{
TyphoonDelegate temp = TyphoonEvent;
if (temp != null)
{
temp(this, new Typhoonclass(place,level));
}
}
}
public class EventReceiver
{
public void EventHandling(object sender, Typhoonclass e)
{
if (e.level < 5)
Console.WriteLine("登陆‘" +e.place+"’的风力为:" +e.level +"级,要稍加防范");
else if (e.level < 10)
Console.WriteLine("登陆‘" +e.place+"’的风力为:" +e.level +"级,船只要回港避风");
else
Console.WriteLine("登陆‘" +e.place+"’的风力为:" +e.level +"级,船只回港人员紧急疏散");
}
public void Subscribe(EventSource source)
{
TyphoonDelegate temp = EventHandling;
source.TyphoonEvent += temp;
}
}
public class Mainclass
{
public static void Main()
{
EventSource source = new EventSource();
EventReceiver receiver = new EventReceiver();
receiver.Subscribe(source);
source.Raise("上海", 3);
source.Raise("浙江",8);
source.Raise("福建", 12);
}
}
======================
三 匿名方法
======================
在下面的示例中,类 TestButton 包含事件 OnClick。派生自 TestButton 的类可以选择响应 OnClick 事件,并且定义了处理事件要调用的方法。可以以委托和匿名方法的形式指定多个处理程序,匿名方法仅适用于C#2.0语法。
//--------------匿名方法1------------------
public delegate void ButtonEventHandler();
class TestButton
{
public event ButtonEventHandler OnClick;
public void Click()
{
OnClick();
}
}
class myclass:TestButton
{
static void Main()
{
TestButton mb = new TestButton();
mb.OnClick += delegate { System.Console.WriteLine("Hello, World!"); };
mb.Click();
}
}
//--------------匿名方法2------------------
public delegate void ButtonEventHandler();
class TestButton
{
public event ButtonEventHandler OnClick;
public void Click()
{
ButtonEventHandler mb = OnClick;
mb();
}
}
class myclass : TestButton
{
static void Main()
{
TestButton sb = new TestButton();
ButtonEventHandler mb = delegate{System.Console.WriteLine("Hello, World!");};
sb.OnClick += mb;
sb.Click();
}
}
======================
四 在接口中声明事件
======================
此示例说明可以在接口中声明一个事件,然后在类中实现它。
public delegate void TestDelegate();
public interface ITestInterface
{
event TestDelegate TestEvent;
void FireAway();
}
public class TestClass : ITestInterface
{
public event TestDelegate TestEvent;
public void FireAway()
{
if (TestEvent != null)
{
TestEvent();
}
}
}
public class MainClass
{
static private void F()
{
System.Console.WriteLine("This is called when the event fires.");
}
static void Main()
{
ITestInterface i = new TestClass();
i.TestEvent += F;
i.FireAway();
}
}
======================
五 事件访问器
======================
可以使用事件访问器声明事件。事件访问器使用的语法非常类似于属性访问器,它使用 add 关键字和代码块添加事件的事件处理程序,使用 remove 关键字和代码块移除事件的事件处理程序。
public delegate void TestEventDelegate();
public class EventSource
{
private TestEventDelegate TestEventHandlers;
public event TestEventDelegate TestEvent
{
add
{
TestEventHandlers += value;
}
remove
{
TestEventHandlers -= value;
}
}
public void RaiseTestEvent()
{
TestEventDelegate temp = TestEventHandlers;
if (temp != null)
{
temp();
}
}
}
public class testMain
{
public void DelegateMethod()
{
System.Console.WriteLine("事件访问器");
}
static void Main()
{
testMain a = new testMain();
EventSource b = new EventSource();
b.TestEvent += a.DelegateMethod;
b.RaiseTestEvent();
}
}
引发事件的类必须具有存储和检索处理程序的机制,才能使用事件访问器。前面的示例使用一个私有委托字段 TestEventHandlers,以及加法和减法赋值运算符在列表中添加和移除处理程序。这与没有使用访问器声明的事件的工作方式极为类似。如果事件接收器使用加法赋值运算符 (+=) 添加事件处理程序,则调用 add 访问器,并且新的处理程序在访问器中可作为局部变量命名值使用。如果使用减法赋值运算符 (-=),则调用 remove 访问器,并且要移除的处理程序可作为局部变量命名值使用。两个访问器都返回 void,因此所有返回语句都不能返回值。无论类是否声明了事件访问器,事件的订阅和取消订阅都使用相同的语法。