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;

  运行结果和上面一样

posted @ 2018-03-20 14:48  Zed_H  阅读(117)  评论(0)    收藏  举报