C#委托、事件和Lambda表达式(2)
1.event关键字
为了简化自定义方法的构建来为委托调用列表增加和删除方法,C#提供了event关键字。
编译器在处理event关键字时 会自动提供注册和注销的方法以及任何必要的委托类型成员变量
定义一个事件分为两个步骤:
(1)定义一个委托类型,它包含在事件触发时将要调用的方法
(2)通过C#event关键字用相关委托声明这个事件
2.一个简单的例子
新增一个控制台程序,添加一个Car类如下:
1 public class Car 2 { 3 public int CurrentSpeed { get; set; } 4 public int MaxSpeed { get; set; } 5 public string PetName { get; set; } 6 7 private bool carIsDead; 8 public Car() 9 { 10 MaxSpeed = 100; 11 } 12 public Car(string name, int maxSp, int currSp) 13 { 14 CurrentSpeed = currSp; 15 MaxSpeed = maxSp; 16 PetName = name; 17 } 18 //这个委托用来与Car的事件协作 19 public delegate void CarEngineHandler(string msgForCaller); 20 21 //可以发送的事件 22 public event CarEngineHandler Exploded; 23 public event CarEngineHandler AboutToBlow; 24 25 public void Accelerate(int delta) 26 { 27 if (carIsDead) 28 { 29 if (Exploded!=null) 30 { 31 Exploded("Sorry, this car is dead..."); 32 } 33 } 34 else 35 { 36 CurrentSpeed += delta; 37 38 //判断是否快不能用了 39 if (10==MaxSpeed-CurrentSpeed&&AboutToBlow!=null) 40 { 41 AboutToBlow("Careful buddy! Gonna blow!"); 42 } 43 44 if (CurrentSpeed>=MaxSpeed) 45 { 46 carIsDead = true; 47 } 48 else 49 { 50 Console.WriteLine("CurrentSpeed={0}", CurrentSpeed); 51 } 52 } 53 } 54 }
program类的实现如下:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Console.WriteLine("**** Fun with Events ****\n"); 6 Car c1 = new Car("SlugBug", 100, 10); 7 8 //注册事件处理程序 9 c1.AboutToBlow += new Car.CarEngineHandler(CarIsAlmostDoomed); 10 Car.CarEngineHandler aboutBlow= new Car.CarEngineHandler(CarAboutToBlow); 11 c1.AboutToBlow += aboutBlow; 12 13 Car.CarEngineHandler d = new Car.CarEngineHandler(CarExploded); 14 c1.Exploded += d; 15 16 Console.WriteLine("**** Speed Up ****"); 17 for (int i = 0; i < 6; i++) 18 { 19 c1.Accelerate(20); 20 } 21 22 //从调用列表中移除CarExploded方法 23 c1.Exploded -= aboutBlow; 24 25 Console.WriteLine("\n#### Speed Up ####"); 26 c1.CurrentSpeed = 20; 27 for (int i = 0; i < 6; i++) 28 { 29 c1.Accelerate(20); 30 } 31 Console.ReadKey(); 32 } 33 34 private static void CarExploded(string msgForCaller) 35 { 36 Console.WriteLine(msgForCaller); 37 } 38 39 private static void CarAboutToBlow(string msgForCaller) 40 { 41 Console.WriteLine(msgForCaller); 42 } 43 44 private static void CarIsAlmostDoomed(string msgForCaller) 45 { 46 Console.WriteLine("=>message from car {0}",msgForCaller); 47 } 48 }
运行结果如下:
3.使用方法组转换简化以上事件的注册
//注册事件处理程序 c1.AboutToBlow += CarIsAlmostDoomed; c1.AboutToBlow += CarAboutToBlow; c1.Exploded += CarExploded; //从调用列表中移除CarExploded方法 c1.Exploded -= CarAboutToBlow;
运行结果和上面的一样
4.创建自定义的事件参数
当我们新建一个窗体应用程序时,单击一个按钮便会有一个相应的事件,其格式如下
private void button1_Click(object sender, EventArgs e) { }
这种事件模式符合微软的推荐,其中第一个参数object表示一个对发送事件的对象的引用,第二个参数表示与该事件相关的信息
创建自定义事件参数如下:
1 public class CarEventArgs:EventArgs 2 { 3 public readonly string msg; 4 public CarEventArgs(string message) 5 { 6 msg = message; 7 } 8 }
修改CarEventHandler委托如下(事件保持不变)
public delegate void CarEngineHandler(object sender, CarEventArgs e);
Accelerate()方法中触发我们的事件时,需提供对当前Car对象的一个引用(通过this关键字)和CarEventArgs类型的一个实例
public void Accelerate(int delta) { if (carIsDead) { if (Exploded != null) { Exploded(this,new CarEventArgs("Sorry, this car is dead...")); } } else { CurrentSpeed += delta; //判断是否快不能用了 if (10 == MaxSpeed - CurrentSpeed && AboutToBlow != null) { AboutToBlow(this,new CarEventArgs("Careful buddy! Gonna blow!")); } if (CurrentSpeed >= MaxSpeed) { carIsDead = true; } else { Console.WriteLine("CurrentSpeed={0}", CurrentSpeed); } } }
对于调用者修改如下:
private static void CarExploded(object sender,CarEventArgs e) { Console.WriteLine("{0} says; {1}", sender, e.msg); } private static void CarAboutToBlow(object sender, CarEventArgs e) { Console.WriteLine("{0} says; {1}", sender, e.msg); }
如果接受者想与发送事件的对象交互,我们可以显示强制类型转换object 如下
private static void CarExploded(object sender,CarEventArgs e) { if (sender is Cars) { Cars c = (Cars)sender; Console.WriteLine("{0} says; {1}", c.PetName, e.msg); } }
5.泛型EventHandler<T>委托
由于很多委托接受object作为第一个参数,EventArgs作为第二个参数,我们可以使用EventHandler<T>类型来进一步简化之前的例子
修改Car类如下(此时不在需要自定义一个委托了)
public event EventHandler<CarEventArgs> Exploded; public event EventHandler<CarEventArgs> AboutToBlow;
运行结果和上面一样