C# 事件(event)

C# 中,事件(event)是一种特殊的委托,用于实现发布-订阅(Publish-Subscribe)模式,广泛用于 GUI 编程、异步通知等场景。事件是委托的封装,用于在一个对象状态发生改变时通知其他对象。本文主要介绍C# 事件(event)。

1、事件

事件是一种专门的代表类型,主要用于消息或通知传递。事件只能从它们发布的类型调用,通常基于EventHandler委托,其中对象代表事件的发送方,System.EventArgs派生类保存有关事件的数据。实现事件和回调方法都是基于委托(delegate)。

2、事件的声明

相关文档C# 委托(delegate)

声明事件需要先声明该事件的委托型类,通过声明的委托类型和event关键字来声明事件。

例如,

public delegate void ClickEventHandler(string args);
// 基于上面的委托定义事件
public event ClickEventHandler ClickEvent;

以上代码简单实现了事件ClickEvent的定义,该事件在声明的时会调用委托。下面看一下通过“订阅者”和“发布者”类实现的完整示例:

例如,

using System;
namespace SimpleEvent
{
  using System;
  /***********发布器类***********/
  public class EventDemo
  {
    private string message;
    public delegate void PrintEventHandler();

    public event PrintEventHandler PrintEvent;
    protected virtual void OnPrinted()
    {
      if ( PrintEvent != null )
      {
        PrintEvent(); /* 事件被触发 */
      }else {
        Console.WriteLine( "事件没被触发" );
        Console.ReadKey(); /* 回车继续 */
      }
    }

    public EventDemo()
    {
      PrintMessage("cjavapy");
    }

    public void PrintMessage( string s )
    {
        message=s;
        OnPrinted();
    }
  }

  /***********订阅器类***********/
  public class SubscribEvent
  {
    public void Print()
    {
      Console.WriteLine( "触发事件" );
      Console.ReadKey(); /* 回车继续 */
    }
  }
  /***********触发***********/
  public class MainClass
  {
    public static void Main()
    {
      EventDemo e = new EventDemo(); /* 实例化对象,第一次没有触发事件 */
      SubscribEvent s = new SubscribEvent(); /* 实例化对象 */
      e.PrintEvent += new EventDemo.PrintEventHandler(s.Print); /* 注册 */
      e.PrintMessage("C#");
      e.PrintMessage("Java");
    }
  }
}

上面示例的事件没有参数,下面看一下委托事件带有参数的情况:

例如,

using System;
namespace SimpleEvent
{
  using System;
  /***********发布器类***********/
  public class EventDemo
  {
    private string message;
    public delegate void PrintEventHandler(string args);

    public event PrintEventHandler PrintEvent;
    protected virtual void OnPrinted(string s)
    {
      if ( PrintEvent != null )
      {
        PrintEvent(s); /* 事件被触发 */
      }else {
        Console.WriteLine( "事件没被触发" );
        Console.ReadKey(); /* 回车继续 */
      }
    }

    public EventDemo()
    {
      PrintMessage("cjavapy");
    }

    public void PrintMessage( string s )
    {
        message=s;
        OnPrinted(s);
    }
  }

  /***********订阅器类***********/
  public class SubscribEvent
  {
    public void Print(string s)
    {
      Console.WriteLine( "触发事件" );
      Console.WriteLine( s );
      Console.ReadKey(); /* 回车继续 */
    }
  }
  /***********触发***********/
  public class MainClass
  {
    public static void Main()
    {
      EventDemo e = new EventDemo(); /* 实例化对象,第一次没有触发事件 */
      SubscribEvent s = new SubscribEvent(); /* 实例化对象 */
      e.PrintEvent += new EventDemo.PrintEventHandler(s.Print); /* 注册 */
      e.PrintMessage("C#");
      e.PrintMessage("Java");
    }
  }
}

3、使用内置委托(推荐写法)

从 .NET 2.0 开始,推荐使用泛型委托 EventHandlerEventHandler<T>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApplication
{
    public class Publisher
    {
        // 定义一个事件,使用内置泛型委托 EventHandler<T>
        public event EventHandler<EventArgs> OnPublish;
    
        // 模拟执行某个操作,然后触发事件
        public void DoWork()
        {
            Console.WriteLine("Working...");
            
            // 触发事件,通知订阅者
            OnPublish?.Invoke(this, EventArgs.Empty);
        }
    }
    
    public class Program
    {
        public static void Main(string[] args)
        {
            // 创建发布者实例
            Publisher p = new Publisher();
    
            // 订阅事件(匿名方法)
            p.OnPublish += (sender, e) =>
            Console.WriteLine("事件触发!");
    
            // 执行操作,触发事件
            p.DoWork();
    
            Console.ReadLine(); // 防止控制台自动关闭
        }
    }

}

推荐阅读
cjavapy编程之路首页