C# 事件进阶(第五章)
我们可以对Car类型做最后一步改进,以符合asp.net里的事件模式。查看基类库中某个类型发送的事件时,会发同底层委托的第一个参数是一个System.Object,第二个参数是一个派生自System.Eventargs的类型。System.Object参数表示不念旧恶对发送事件的对象(例子如Car对象)的引用,第二个参数则表示与该事件相关的信息。System.Eventargs基类表示一个不发送任何自定义信息的事件:
public class EventArgs { public static readonly System.EventArgs Empty; public EventArgs(){} }
对简单的事件来说,我们可以直接传递一个EventArgs实例。但如果要传递自定义数据,应该构建一个派生自EventArgs的类。在这个示例中,假定我们有一个名为CarEventArgs 的类,它保存一个字符串,表示要发送给接收者的信息:
public class CarEventArgs:EventArgs { public static readonly string msg; public EventArgs(string message) {msg=message;} }
这样,我们就可以修改CarEventHandler委托了,如下所示(事件将保持不安):
public class Car { public delegate void CarEventHandler(object sender,CarEventArgs e); ... }
当在Accelerate()方法中触发我们的事件时,需要提供对当前Car对象的一个引用和CarEventArgs类型的一个实例:
public void Accelerate(int delta) { if(carIsDead) { if(Exploded!=null) Exploded(this,new CarEventArgs("Sorry,this car is dead..")); } ... }
在调用者,我们要做的就是修改事件处理程序来接收传入的参数,并通过只读字段获取消息。例子如:
public static void CarAboutToBlow(object sender,CarEventArgs e) { Console.WriteLine("{0} says:{1}",sender,e.msg); }
如果接收者想与发送事件的对象交互,我们可以显式强制类型转换System.Object。这样,如果要在Car对象将遇到它的创建者时“关闭广播”,可以创建一个如下所示的事件处理程序:
public static void CarIsAlmostDoomed(string msg) { //安全起见,在强制类型转换前做一次运行时检查 if(sender is Car) { Car c=(Car) sender; c.Cranktunes(false); } Console.WriteLine("Critical Message from {0}:{1}",sender,e.msg); }
泛型EventHandler<T>委托
由于很多自定义委托接受对象作为第一参数,EventArgs派生类型作为第二个参数,我们可以通过使用泛型EventHandler<T>类型来进一步简化之前的示例子,T就是自定义的EventArgs类型。
public calss Car { public event EventHandler<CarEventArgs> Exploded; publci event EventHandler<CarEventArgs> AboutToBlow; ... }
static void main(string[] args) { Console.Write("####Delegates as events ####\n"); Car cl=new Car("SlugBug",100,10); //注册事件处理程序 cl.OnAboutToBlow +=new EventHandler<CarEventArgs>(CarIsAlmostDoomed); cl.OnAboutToBlow +=new EventHandler<CarEventArgs>(CarAboutToBlow); EventHandler<CarEventArgs d=new EventHandler<CarEventArgs>(CarExploded); c1.Exploded +=d; ... }
C#匿名方法
当一个调用者想监听传进来的事件时,它必须定义一个唯一的与相关联委托签名匹配的方法:
class Program { static void Main(string[] args) { SomeType t=new SomeType(); //假定"SomeDelegate"指向不带参数且无返回值的方法 t.SomeEvent += new SomeDelegate(MyEventHandler); } //一般,它仅被SomeDelegate对象调用 public static void MyEventHandler() { //事件触发时执行某些操作 } }
稍微观察会发现,MyEventHandler()这样的方法很少会被调用委托之外的任保程序所调用。从生产效率的角度来说,手工定义一个由委托对象调用的方法显得有点繁锁,不会很受欢迎。
为解决这一问题,现在可以在事件注册时直接将一个委托与一段代码相关联。这种代码的正名多称为匿名方法。
class Pragram { static void main(string[] args) { Console.Write("####Anonymous Methods ####\n"); Car cl=new Car("SlugBug",100,10); //注册事件处理程序为匿名方法 cl.OnAboutToBlow +=delegate(object sender,CarEventArgs e) { Console.WriteLine("Eek! Going too fast!"); }; cl.OnAboutToBlow +=delegate(object sender,CarEventArgs e) { Console.WriteLine("Message from Car:{0}",e.msg); }; cl.Exploded+=delegate(object sender,CarEventArgs e) { Console.WriteLine("Fatal Message from Car:{0}",e.msg); }; ... } }
方法组转换
public class SimpleMath { public delegate void MathMessage(string msg); public event MathMessage ComputationFinished; public int Add(int x,int y) { ComputationFinshed("AddingComplete"); return x+y; } } //如不用匿名方法,可以如下所示处理ComputationComplete事件 class Program { static void Main(string[] args) { SimpleMath m=new SimpleMath(); m.ComputationFinish +=new SimpleMath.MathMessage(ComputationFinishedHandler); Console.WriteLine("10+10 is{0}",m.Add(10,10)); Console.ReadLine(); } static void ComputationFinishedHandler(string msg) { Console.WriteLine(msg); } }
然而,我们可以用一个特定的事件来注册事件处理程序:
m.Computationfinished +=ComputationFinishedHandler;
请注意,我们没有直接新建关联的委托类型,而是简单指定了一个匹配委托预期签名的方法,在本例中这个方法传入一个System.String而且没有返回值。要知道C#编译器这时仍会保证类型安全。
显式转换一个事件处理程序为期关联委托的一个实例也是可能的。