【C#进阶】C# 事件

2024-03-01 7339阅读
序号系列文章
15【C#进阶】C# 属性
16【C#进阶】C# 索引器
17【C#进阶】C# 委托

文章目录

  • 前言
  • 1、什么是事件
    • 1.1、发布订阅模型的说明
    • 2、事件的声明
    • 3、事件的使用
      • 3.1、使用基类中的事件
      • 3.2、接口中定义事件
      • 3.3、自定义方法访问事件
      • 4、事件与委托的异同:
      • 前言

        🌍 hello大家好啊,我是哈桑。本文为大家介绍 C# 中的事件。


        1、什么是事件

        事件本质上来讲是一种特殊的多播委托1,只能从声明它的类中进行调用。事件通常用于表示用户操作,例如单击按钮或图形用户界面中的菜单选项。C# 中常常会使用事件来实现线程之间的通信。

        1.1、发布订阅模型的说明

        在 C# 中,类或对象可以通过事件向其他类或对象通知发生的相关事情。这种模式通常称为发布订阅模型,发送(或引发)事件的类称为“发布者”,接收(或处理)事件的类称为“订阅者”。

        对发布者和订阅者的解释说明:

        发布者: 一个创建了事件和委托定义的对象,同时也包含了事件和委托之间的联系与具体行为。发布者的任务就是执行这些事件,并通知程序中的其它对象。

        订阅者: 一个接收事件并提供事件处理程序的对象。订阅者中的方法(事件处理程序)用于分配给发布者中的委托。

        简单的来说,发布者确定何时引发事件,而订阅者确定对事件作出何种响应。

        代码示例:(简单实现)

        using System; 
        // 发布者类 
        public class PublisherClass
        {
            // 和事件搭配的委托
            public delegate void PubDelegate();
            // 定义事件 
            public event PubDelegate PubEvent;
            // 编写处理事件的具体逻辑
            public void EventHandling()
            {
                if (PubEvent == null)
                {
                    Console.WriteLine("需要注册事件的啊");
                }
                else
                {
                    // 执行注册的事件 
                    PubEvent(); 
                }
            }
        }
        // 订阅者类 
        public class SubscriberClass
        {
            public void printout()
            {
                Console.WriteLine("执行了订阅者类中的事件。");
                Console.ReadLine(); 
            }
        }
        public class Program
        {
            static void Main()
            {
                // 实例化对象 
                PublisherClass p = new PublisherClass();
                SubscriberClass s = new SubscriberClass();
                // 执行事件 
                p.EventHandling();
                // 注册事件 
                p.PubEvent += new PublisherClass.PubDelegate(s.printout); 
                // 执行事件 
                p.EventHandling();  
            }
        }
        

        运行结果:

        【C#进阶】C# 事件 第1张

        在上例中,创建了发布者类 PublisherClass 和订阅者类 SubscriberClass,直接把订阅者类中的 printout 方法传递给了 PublisherClass.PubEvent 方法用于执行。(发布订阅模型的简单思路就是这样, 在正式的项目中程序之间的交互与通信表现得更加复杂。)

        2、事件的声明

        首先需要注意的是,因为事件的本质还是委托,所以要声明一个事件之前必须先声明一个相对应的委托。

        以上面示例的代码来说明:

        // 和事件搭配的委托
        public delegate void PubDelegate();
        

        在 C# 中,事件需要使用关键字 event,事件的声明语法可以总结为如下所示:

        event

        • Access Specifier: 访问说明符
        • event: 声明事件必须要有的关键字
        • Delegate: 分配给事件的委托(事先声明好的)
        • Event Name: 事件的名称

          示例代码:

          // 定义事件 
          public event PubDelegate PubEvent;
          

          3、事件的使用

          事件的基本使用在上面的发布订阅模型的说明中已经演示了,这里不再赘述。接下来以一个新的示例来说明事件在基类、接口中的定义和如何自定义方法访问事件的操作。

          3.1、使用基类中的事件

          子类可以继承使用基类中已经声明的事件,像这种使用基类中的事件的模式广泛用于 .NET 类库中的 Windows 窗体2类。 以一个简单的程序来演示子类如何使用基类中的事件。

          代码示例:

          namespace BaseClassEvents 
          {
              using System;
              // 基类事件发布者
              public abstract class Shape
              {
                  protected double _area;
                  public double Area
                  {
                      get { return _area; }
                      set { _area = value; }
                  }
                  // 声明一个与事件搭配的委托
                  public delegate void ShapeDelegate();
                  // 声明基类中的事件 
                  public event ShapeDelegate ShapeEvent;
                  // 抽象方法
                  public abstract void Drow();
                  public abstract void GetArea();
                  // 执行已经注册的事件 
                  public void EventHandling()
                  {
                      if (ShapeEvent != null)
                      {
                          ShapeEvent();
                      }
                      else
                      {
                          Console.WriteLine("需要注册事件的");
                      }
                  }
              }
              // 圆形 
              public class Circle : Shape
              {
                  private double _radius;
                  public Circle(double radius)
                  {
                      _radius = radius;
                      _area = 3.14 * _radius * _radius;
                  }
                  public override void Drow()
                  {
                      Console.WriteLine("绘制了一个圆形");
                  }
                  public override void GetArea()
                  {
                      Console.WriteLine($"圆形的面积为{Area}");
                  }
              }
              // 矩形
              public class Rectangle : Shape
              {
                  private double _length;
                  private double _width;
                  public Rectangle(double length, double width)
                  {
                      _length = length;
                      _width = width;
                      _area = _length * _width;
                  }
                  public override void Drow()
                  {
                      Console.WriteLine("绘制了一个矩形");
                  }
                  public override void GetArea()
                  {
                      Console.WriteLine($"矩形的面积为{Area}");
                  }
              }
              // 将形状的行为添加道基类的事件里去 
              public class ShapeContainer
              {
                  public void AddMethod(Shape shape)
                  {
                      shape.ShapeEvent += shape.Drow;
                      shape.ShapeEvent += shape.GetArea;
                      // 执行基类中已经注册的事件 
                      shape.EventHandling();
                  }
              }
              public class Program
              {
                  static void Main(string[] args)
                  {
                      var circle = new Circle(11);
                      var rectangle = new Rectangle(11, 11);
                      var container = new ShapeContainer();
                      container.AddMethod(circle);
                      container.AddMethod(rectangle);
                  }
              }
          }
          

          运行结果:

          【C#进阶】C# 事件 第2张在上面的示例中,创建了 Circle 和 Rectangle 两个形状类并继承了 Shape 基类, 在 ShapeContainer.AddMethod 方法中使用 shape 参数将指定类方法添加基类的 ShapeEvent 事件中,这样就可以在子类中使用基类中的 EventHandling 方法,以此达到使用基类中的事件的目的。

          3.2、接口中定义事件

          不仅是在类中,在接口中也可以声明事件,称为接口事件。接口事件的实现和接口上的方法或属性的实现是一样的,以一个示例来说明如何在类中实现接口事件。

          代码示例:

          namespace ImplementInterfaceEvents
          {
              public interface IDrawingObject
              {
                  // 所有继承该接口的对象都需要创建 ShapeChanged 事件
                  event EventHandler ShapeChanged;        
              }
              public class MyEventArgs : EventArgs
              {
                  // 构造方法 
                  public MyEventArgs()
                  {
                      Console.WriteLine("执行了 MyEventArgs 类");
                  }
              }
              public class Shape : IDrawingObject
              {
                  public event EventHandler ShapeChanged;
                  public void ChangeShape()
                  {
                      // 在活动开始前做点什么… 
                      MyEventArgs m = new MyEventArgs();
                      OnShapeChanged(m);
                      // 或者事后在这里做点什么。 
                  }
                  protected virtual void OnShapeChanged(MyEventArgs e)
                  {
                      ShapeChanged?.Invoke(this, e);
                  }
              }
              public class Program
              {
                  static void Main(string[] args)
                  {
                      Shape s = new Shape();
                      s.ChangeShape();
                  }
              }
          }
          

          运行结果:

          【C#进阶】C# 事件 第3张

          在上面的示例,Shape 类继承 IDrawingObject 接口并实现了 ShapeChanged 事件。在 ChangeShape 方法中将 MyEventArgs 方法注册到了 ShapeChanged 事件中并调用。

          3.3、自定义方法访问事件

          在大多数情况下,是无需提供自定义事件访问器的。但是某些特殊情况下,就需要自定义事件访问器,比方说当类继承自两个或多个接口,且每个接口都具有相同名称的事件。这时就必须为至少其中一个事件提供显式接口实现。 为事件编写显式接口实现时,还必须编写 add 和 remove 事件访问器。当遇到这种情况时就需要自己定义一个事件访问器。

          通过提供自己的访问器,可以指定两个事件是由类中的同一个事件表示,还是由不同事件表示。 例如,如果根据接口规范应在不同时间引发事件,可以在类中将每个事件与单独实现关联。

          代码示例:

          namespace ImplementInterfaceEvents
          {
              public interface IDrawingObject
              {
                  // 所有继承该接口的对象都需要创建 ShapeChanged 事件
                  event EventHandler ShapeChanged;
              }
              public class MyEventArgs : EventArgs
              {
                  // 构造方法 
                  public MyEventArgs()
                  {
                      Console.WriteLine("执行了 MyEventArgs 类");
                  }
              }
              public class Shape : IDrawingObject
              {
                  //为每个接口事件创建一个事件 
                  event EventHandler DrawEvent;
                  // 自定义实现 
                  event EventHandler IDrawingObject.ShapeChanged
                  {
                      add { DrawEvent += value; }
                      remove { DrawEvent -= value; }
                  }
                  public void ChangeShape()
                  {
                      // 在活动开始前做点什么… 
                      MyEventArgs m = new MyEventArgs();
                      OnShapeChanged(m);
                      // 或者事后在这里做点什么。 
                  }
                  protected virtual void OnShapeChanged(MyEventArgs e)
                  {
                      DrawEvent?.Invoke(this, e);
                  }
              }
              public class Program
              {
                  static void Main(string[] args)
                  {
                      Shape s = new Shape();
                      s.ChangeShape();
                  }
              }
          }
          

          在接口事件的示例基础上进行的改动:

          【C#进阶】C# 事件 第4张

          点击了解更多自定义事件访问器的使用。

          4、事件与委托的异同:

          • 相同点:
            • 事件其实是一个多播委托,本质上是一样的。
            • 不同点:
              • 可调用位置不同:事件只能在声明事件的类中才能调用,而委托无论是在类的内部还是外部都可以调用。
              • 可使用符号不同:事件只能使用 += 和 -= 符号来订阅和取消订阅,但是委托不仅可以使用 += 和 -= 符号还可以使用 = 符号进行方法分配。

                点击了解更多事件的使用。


                结语

                🌎 以上就是 C# 事件的介绍啦,希望对大家有所帮助。感谢大家的支持。


                1. 多播委托: 就是一个委托同时绑定多个方法,多播委托也叫委托链,委托组合。 ↩︎

                2. Windows 窗体: Windows 窗体是用于生成 Windows 桌面应用的 UI 框架。 ↩︎


    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]