c#学习笔记03——委托和事件

  • 委托:一种引用类型,这种类型可以用来定义方法签名,从而使用委托实现将方法作为参数传递给其他方法。类似于C++中的函数之争,使用委托使程序员可以将方法引用封装在委托对象内
  1. 定义和声明委托:
    1 delegate 返回值 委托名(参数列表);
    2 eg:
    3 public delegate void SayHelloDelegate(string name);
  2. 使用委托:委托其实通过返回值和参数列表来定义方法和签名。任何与委托具有相同返回值和参数列表(签名)的方法都可以赋给该委托。
    1 public delegate void SayHelloDelegate(string name);//声明委托
    2 public viod SayHelloEnglish(string name)//声明方法
    3 {
    4         Console.WriteLine("Hello,{0}",name);
    5 }
    6 SayHelloDelegate s=SayHelloEnglish;//把方法赋予委托对象
    View Code

     委托实例化的几种形式:

     1 1.使用new关键字
     2 SayHelloDelegate s=new SayHelloDelegate(SayHelloEnglish);
     3 2.直接赋值
     4 SayHelloDelegate s=SayHelloEnglish;
     5 3.使用匿名方法
     6 SayHelloDelegate s=delegate(string name)
     7 {
     8          Console.WriteLine("Hello,{0}",name);
     9 }
    10 4.使用Lambda表达式,例如:
    11 SayHelloDelegate s=name=>
    12 {
    13          Console.WriteLine("Hello,{0}",name);
    14 }
    1 委托实例化后,就可以像调用方法一样调用委托。eg:
    2 s("John");
  3. 多播委托:一个委托对象可以包含多个方法。调用多播委托时,按赋值的顺序一次执行每个方法。
    1. 实现多播委托
      1. 使用运算符+实现:委托对象的一个有用属性,可以使用+运算符将多个对象分配给一个委托实例。这里运算符+操作对象只能是委托对象,不能使方法签名。
         1 public delegate void SayHelloDelegate(string name);
         2 public void SayHelloEnglish(string name)
         3 {
         4        Console.WriteLine("Hello,{0}",name);
         5 }
         6 public void SayHelloChinese(string name);
         7 {
         8         Console.WriteLine("你好,{0}",name);
         9 }
        10 SayHelloDelegate s1=new SayHelloDelegate(SayHelloEnglish);
        11 SayHelloDelegate s2=SayHelloChinese;
        12 SayHelloDelegate s3=s1+s2;//操作符两边只能是委托对象
        View Code
      2. 使用运算符+=
         1 public delegate void SayHelloDelegate(string name);
         2 public void SayHelloEnglish(string name)
         3 {
         4        Console.WriteLine("Hello,{0}",name);
         5 }
         6 public void SayHelloChinese(string name);
         7 {
         8         Console.WriteLine("你好,{0}",name);
         9 }
        10 SayHelloDelegate s=new SayHelloDelegate(SayHelloEnglish);
        11 SayHelloDelegate s+=SayHelloChinese;//右边的操作对象是方法签名
        View Code
    2. 特点:
      1. 多播委托可以包含多个方法
      2. 多播委托包含的方法必须返回viod,否则会抛出run-time exception
      3. 只能合并相同类型的委托
    3. 从多播委托中移除组件
      1. 使用运算符-,两侧必须为委托对象
      2. 使用运算符-=,右侧可以为方法名
  4. 匿名方法:相对于命名方法(以前接触的方法都是命名方法,都是包含方法名的)来说,顾名思义,就是没有命名的方法,就是作为一个整体的一些代码块。想要实现匿名方法,使用委托是唯一途径,把整个代码块作为参数赋予委托对象,然后调用委托对象,从而实现方法的执行。
    1 //创建委托
    2 delegate void Del(string name);
    3 //实现匿名方法
    4 Del d=delegate(string name)
    5 {
    6 
    7     //code block
    8 };

                      summary:

    1. 匿名方法的定义是以关键字delegate开始,后面跟着参数列表和方法主体。
    2. 使用匿名方法,可以省去一个编写方法的过程,从而使代码看起来更简洁。
    3. 使用匿名方法可以减少实例化委托的代码系统开销。

                      attention:

    1. 在匿名方法中不能使用跳转语句跳转到该匿名方法的外部,匿名方法外部的跳转语句不能跳转到匿名方法的内部。
    2. 匿名方法内部不能访问不安全代码。
    3. 不能再匿名方法外部使用ref和out参数,但可以使用在匿名方法外部定义的其他变量。
       1 public delegate void MyDelegate();
       2 class Program
       3 { 
       4      static void Main(string[] args)
       5      {
       6             Program p=new Program();
       7             for(int i=1;i<=5;i++)
       8                    p.TestInstanceDataMembers();
       9              Console.ReadLine();
      10       }
      11       //测试方法
      12       public void TestInstanceDataMembers()
      13       {
      14              //声明匿名方法并赋值给对象
      15              MyDelegate d=delegate
      16               {
      17                     //操作外部局部变量
      18                     Console.WriteLine("Count:{0}",++m_iCount);
      19                };
      20               d();//执行方法
      21       }
      22       public int m_iCount=0;//定义外部变量
      23 }
      24
      View Code
      1 Count: 1
      2 Count: 2
      3 Count: 3
      4 Count: 4
      5 Count: 5
      View Code
  1. 委托中的协变和逆变:将方法签名与委托类型匹配时,协变和逆变提供了一定程度的灵活性。协变允许方法具有的派生返回类型比委托中定义的更多。逆变允许方法具有的派生参数类型比委托类型中的更少
    1. 协变方法签名与委托类型匹配的灵活性体现在方法返回类型上。eg:
       1 Shape
       2 {
       3    //类主体
       4 }
       5 Point:Shape
       6 {
       7    //类主体
       8 }
       9 delegate Shape DoDelegate();
      10 static Shape FirstDo()
      11 {
      12    return null;
      13 }
      14 static Point SecondDo()
      15 {
      16    return null;
      17 }
      18 DoDelegate Do1=FirstDo;
      19 //协变允许实现这样的委托
      20 DoDelegate Do2=SecondDo;
      View Code

      协变允许返回类型更多体现在类的派生上

    2. 逆变使方法签名与委托类型匹配的灵活性体现在参数上。eg:
       1 System.DateTime lastActivity;
       2 public From1()
       3 {
       4      InitializeComponent();
       5      lastActivity=new System.DateTime();
       6      this.textBox1.KeyDown+=this.MultiHandler;//触发KeyEventArgs
       7      this.botton1.MouseClick+=this.MultiHandler;//触发MouseEventArgs
       8 }
       9 private void MultiHandler(object sender,System.EvenArgs e)
      10 {
      11      lastActivity=System.DateTime.Now;
      12 }
      View Code

       以上代码中只创建一个接收EventArgs输入参数的事件处理程序,然后,可以将该处理程序与发送MouseEventArgs类型作为参数的Botton.MouseClick事件一起使用,也可以将该处理程序与发送KeyEventArgs参数的TextBox.KeyDown事件一起使用。

  2. 回调函数:委托实例化后,就可以将其作为参数进行传递,方法遍可以将一个委托作为参数来接受,并且以后可以调用该委托,这称为回调函数
     1 delegate void Del(string name);//定义委托
     2 void Do(string)//定义方法
     3 {
     4 }
     5 void FirstDo(string s,Del del)//把委托作为参数传递进来
     6 {
     7      del(s);//调用委托
     8 }
     9 Del del=new Del(Do);//初始化委托
    10 FirstDo(“你好",del);//调用方法
    View Code
    1. 属于工作流的一个部分
    2. 必须按照工作流指定的调用约定来声明(定义)
    3. 它的调用时机由工作流决定,回调函数的实现者不能直接调用回调函数来实现工作流的功能。
    4. 使用回调机制,可以为工作流实现扩展。可以把工作流中需要用户干预的,或者需要提供给用户的数据以回调的模式提供给用户。而用户不需要知道整个工作的流程,只需要知道回调函数的说明就可以使用工作流模块提供的功能,这对信息的隐藏也是有作用的。
       1 class SumClass
       2 {
       3     public delegate int Sum(int num1,int num2);委托
       4     public int SumAll(int num1,int num2,Sum sun)//把委托实例作为参数传递
       5     {
       6            return sun(num1,num2);//回调函数
       7      }
       8 }
       9 class Program
      10 {
      11      static void Main(string[] args)
      12      {
      13           Program prog=new Program();
      14           SumClass sumClass=new SumClass();
      15           int result=sunClass.SumAll(1,2,prog.GetSum);//回调函数GetSum
      16           Console.WriteLine(result.ToString());
      17           Console.ReadLine();
      18       }
      19       private int GetSum(int a,int b)
      20       {
      21             return a+b;
      22       }
      23 }
      View Code
事件:所有与用户交互的过程基本上都是事件触发和处理的过程。
  1. 定义和声明事件:事件其实就是消息,事件的本质就是对消息的封装,用作对象之间的通信:事件的发起方称为事件发送器,事件的接收方称为事件的接收器。在C#中提供了一个名为EventHandler的预定义委托,专用于表示不生成数据的事件的事件处理方法
    这个委托的预定义如下:
    public delegate void EventHandler(
       object sender,//引用引发事件的实例
       EventArgs e//从EventArgs类型派生,保存事件数据
    )

     EventHandler专用于表示不生成数据的事件的事件处理程序方法。如果事件生成数据,则必须提供自定义数据类型,并创建一个委托,其中第二个参数为自定义参数类型。若要将事件与处理事件的方法关联,还要向事件添加委托的实例,这时需要使用关键字event来声明。

    1 //访问修饰符event EventHandler 事件名
    2 
    3 public event EventHandler NoDataEventHandler;
    4 //定义了没有数据的事件成员

     

  2. 定义事件处理程序:定义了事件成员,就可以把事件处理程序关联到事件上。事件处理程序委托会被绑定到系统引发事件时要执行的方法。事件处理程序会被添加到事件中,以便当事件引发时,事件处理程序能够调用它的方法,这就是订阅事件。订阅事件的处理过程如下:
    1. 编写事件处理程序
      1 void HandleCustomEvent(object sender,CustomEventArgs a)
      2 {
      3           //处理程序
      4 }

       

    2. 使用加法赋值运算符+=来为事件附加处理程序,eg:
      1 publisher.RaiseCustomEvent+=HandlerCustomEvent;

       当然如果用匿名方法或Lambda表达式,可以不用事先编写事件处理程序,直接把匿名方法或Lambda表达式绑定到事件上。事件是一种特殊类型的委托,支持多播委托,因此可以把多个事件处理程序绑定到一个事件中。

    3. 创建并使用事件
       1 using cs002;
       2 using System;
       3 using System.Collections.Generic;
       4 using System.Linq;
       5 using System.Text;
       6 using System.Threading.Tasks;
       7 
       8 
       9 namespace cs002
      10 {
      11     public class A
      12     {
      13         public delegate void EventHandler(object sender, EventArgs e);//定义委托
      14         public event EventHandler a;//声明事件
      15         public void Run(EventArgs e)//引发事件
      16         {
      17             Console.WriteLine("产生一个事件。");
      18             a(this, e);//处理当前引发的事件
      19         }
      20     }
      21     class B
      22     {
      23         public B(A a)
      24         {
      25             a.a += new A.EventHandler(this.b); //订阅事件处理程序
      26         }
      27         private void b(object sender,EventArgs e)//事件处理的方法
      28         {
      29             Console.WriteLine("处理事件!");
      30             Console.Read();
      31         }
      32     }
      33     class prg
      34     {
      35         public static void Main(string[] args)
      36         {
      37             A a = new A();
      38             B b = new B(a);
      39             EventArgs e = new EventArgs();
      40             a.Run(e);
      41         }
      42     }
      43 }
      View Code

       

    4. 从EventArgs类派生:EventArgs类是包含事件数据的类的基类。此类不包含事件数据,在事件的引发时不向事件处理程序传递状态信息的事件会使用此类。EventArgs类本身并没有什么可介绍的,它只提供了一个只读字段Empty,表示事件没有数据。如果事件处理需要状态信息,则应用程序必须从EventArgs类派生一个类来保存数据。例如创建一个KeyEventArgs类,该类保存键盘按键事件中要包含的按键信息。
       1 internal class KeyEventArgs:EventArgs
       2 {
       3      private char keyChar;//按键信息
       4      public KeyEventArgs(char keyChar):base()
       5       {
       6              this.keyChar=keyChar;
       7        }
       8       public char KeyChar//按键信息
       9        {
      10              get
      11               {
      12                     return keyChar;
      13                }
      14          }
      15 }
      View Code

       创建并使用派生事件

       1 using cs002;
       2 using System;
       3 using System.Collections.Generic;
       4 using System.Linq;
       5 using System.Text;
       6 using System.Threading.Tasks;
       7 
       8 
       9 namespace cs002
      10 {
      11     //触发火警事件
      12   public class FireEventArgs:EventArgs
      13     {
      14         public FireEventArgs (string room,int ferocity)
      15         {
      16             this.room = room;
      17             this.ferocity = ferocity;
      18         }
      19         public string room;
      20         public int ferocity;//火势等级
      21     }
      22     public class FireAlarm
      23     {
      24         public delegate void FireEventHandler(object sender, FireEventArgs fe);
      25         //创建火警事件委托
      26         public event FireEventHandler FireEvent;
      27         //触发火警事件 和委托相联系/类型相同
      28         public void ActivateFireAlarm(string room,int ferocity)
      29         {
      30             FireEventArgs fireArgs = new FireEventArgs(room, ferocity);
      31             //调用委托
      32             FireEvent(this, fireArgs);
      33         }
      34     }
      35     //火警事件处理类
      36     class FireHandlerClass
      37     {
      38         public FireHandlerClass (FireAlarm fireAlarm)
      39         {
      40             //订阅火警事件处理程序
      41             fireAlarm.FireEvent += new FireAlarm.FireEventHandler(ExtinguishFire);//多播委托
      42         }
      43         //火警事件处理程序
      44         void ExtinguishFire(object sender,FireEventArgs fe)
      45         {
      46             Console.WriteLine("\n火警事件是由{0}引发的:", sender.ToString());
      47             if (fe.ferocity < 2)
      48                 Console.WriteLine("发生在{0}的火警是没有问题的,用水就可以浇灭。", fe.room);
      49             else if (fe.ferocity < 5)
      50                 Console.WriteLine("要使用灭火器才能扑灭{0}的大火。", fe.room);
      51             else
      52                 Console.WriteLine("发生在{0}的大火已经失控,请通知政府部门!", fe.room);
      53         }
      54     }
      55     public class program
      56     {
      57         public static void Main(string[] args)
      58         {
      59             //创建火警对象
      60             FireAlarm myFireAlarm = new FireAlarm();
      61             //创建火警事件处理程序对象
      62             FireHandlerClass myFireHandler = new FireHandlerClass(myFireAlarm);
      63             //触发火警事件
      64             myFireAlarm.ActivateFireAlarm("厨房", 3);
      65             myFireAlarm.ActivateFireAlarm("书房", 1);
      66             myFireAlarm.ActivateFireAlarm("车库", 5);
      67         }
      68     }
      69 }
      View Code

       

    5. 在派生类中引发基类事件:事件作为类的成员,一般都被定义为public类型,但是派生类并不能直接调用基类中声明的事件。但可以在包含该事件的基类中创建一个受保护的调用方法。通过调用或重写此调用方法,派生类便可简介调用该事件。
       1 using cs002;
       2 using System;
       3 using System.Collections.Generic;
       4 using System.Linq;
       5 using System.Text;
       6 using System.Threading.Tasks;
       7 
       8 
       9 namespace cs002
      10 {
      11     public class ShapeEventArgs:EventArgs
      12     {
      13         private double newArea;
      14         public ShapeEventArgs(double d)
      15         {
      16             newArea = d;
      17         }
      18         public double NewArea
      19         {
      20             get { return newArea; }
      21         }
      22     }
      23   //定义图形类Shape,在该类中定义事件
      24   public abstract class Shape
      25     {
      26         public delegate void shapeEventHandler(object sender, ShapeEventArgs e);
      27         public double area;
      28         public event shapeEventHandler ShapeChanged;
      29         public abstract void Draw();
      30         //受保护的触发事件的方法
      31         protected virtual void OnShapeChanged(ShapeEventArgs e)
      32         {
      33             shapeEventHandler handler = ShapeChanged;
      34             if(handler!=null)
      35             {
      36                 handler(this, e);
      37             }
      38         }
      39     }
      40     public class Circle:Shape
      41     {
      42         private double radius;
      43         public Circle(double d)
      44         {
      45             radius = d;
      46             area = 3.14 * d * d;
      47         }
      48         public void Update(double d)
      49         {
      50             radius = d;
      51             area = 3.14 * d * d;
      52             OnShapeChanged(new ShapeEventArgs(area));//触发事件
      53         }
      54         //重写受保护的触发事件的方法
      55         protected override void OnShapeChanged(ShapeEventArgs e)
      56         {
      57             //调用基类的方法以触发事件
      58             base.OnShapeChanged(e);
      59         }
      60         public override void Draw()
      61         {
      62             Console.WriteLine("画一个圆");
      63         }
      64     }
      65     //事件处理程序
      66     public class ShapeHandler
      67     {
      68         public ShapeHandler (Shape s)
      69         {
      70             //订阅事件
      71             s.ShapeChanged += new Shape.shapeEventHandler(HandleShapeChanged);
      72         }
      73         //事件处理程序
      74         private void HandleShapeChanged(object sender,ShapeEventArgs e)
      75         {
      76             Shape s = (Shape)sender;
      77             //显示图形信息
      78             Console.WriteLine("图形更改事件是由{0}引起的,图形的面积更新后是:{1}", sender.ToString(), e.NewArea);
      79             //绘制图形
      80             s.Draw();
      81         }
      82     }
      83     public class program
      84     {
      85         public static void Main(string[] args)
      86         {
      87             Circle c1 = new Circle(54);
      88             Console.WriteLine("更新前的图形面积是:{0}", c1.area);
      89             ShapeHandler myShapeHandler = new ShapeHandler(c1);
      90             c1.Update(57);
      91             System.Console.WriteLine("Press any key to exit.");
      92             System.Console.ReadKey();
      93         }
      94     }
      95 }
      View Code

       

    6. 实现接口事件
       1 public interface Iobject//定义接口
       2 {
       3      event EventHandler OnChangedEventArgs;
       4 }
       5 public class MyEventArgs:EventArgs
       6 {
       7 }
       8 public class A:Iobject
       9 {
      10       public event EventHandler OnChangedEventArgs;
      11       void ChangedA{
      12            OnChanged(new MyEventArgs(/*arguments*/));
      13               }
      14 protected virtual void OnChanged(MyEventArgs e)
      15 {
      16         if(OnChangedEventArgs!=null)
      17           {
      18              OnChangedeEventArgs(this,e);
      19            }
      20 }
      21 }

       

 

          

转载于:https://www.cnblogs.com/yuelien/p/6661462.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值