C#事件(Event): 发布符合 .NET Framework Guidelines 的事件

本文翻译整理自:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-publish-events-that-conform-to-net-framework-guidelines

以下的示例展示了怎样添加符合 .NET Framework 模式事件到自定义的类和结构体中。.NET Framework中所有的类库都是基于 EventHandler delegate,定义如下:

public delegate void EventHandler(object sender, EventArgs e);

尽管在类中定义的事件可以是任何有效的 delegate 类型,甚至 delegate 可以返回一个类型,但是通常建议事件以 .NET Framework 模式为准,基于 EventHandler

 

发布基于 EventHandler 模式的事件

  1. 如果需要传递额外的数据,就需要定义对于发布者和订阅者都需要的参数类,下面的类包含了一个额外的 message 字段,可以用来在发布者和订阅者之间传递信息,如下
    public class CustomEventArgs : EventArgs
    {  
        public CustomEventArgs(string s)  
        {  
            msg = s;  
        }  
        private string msg;  
        public string Message  
        {  
            get { return msg; }  
        }   
    }

     

  2. 在发布类中定义一个 delegate 类型,这里使用非泛型,定义 CustomEventHandler 类型,第一个参数是 object 类型,第二个参数是自定义的 CustomEventArgs 类型(用于传递自定义的数据) ,如下
    public delegate void CustomEventHandler(object sender, CustomEventArgs a); 

     

  3. 在发布类中,使用下面的一种方法声明事件

    第一种:如果不需要传递额外的数据,不需要自己定义 delegate 类型,在 System 命名空间中已经有预定义的事件类型 EventHandler ,直接使用即可:
    public event EventHandler RaiseCustomEvent;


    第二种:需要传递额外数据,使用非泛型版本,使用自己定义的 delegate 类型

    public event CustomEventHandler RaiseCustomEvent; 


    第三种:需要传递额外数据,使用泛型版本(.NET Framework 2.0 及以后版本可用)的 EventHandler

    public event EventHandler<CustomEventArgs> RaiseCustomEvent;  

     

  完整示例:

namespace DotNetEvents
{
    using System;
    using System.Collections.Generic;

    // 定义事件参数:包含一个额外的 message 字段
    public class CustomEventArgs : EventArgs
    {
        public CustomEventArgs(string s)
        {
            message = s;
        }
        private string message;

        public string Message
        {
            get { return message; }
            set { message = value; }
        }
    }

    // 发布者
    class Publisher
    {
        // 使用 EventHandler<T> 声明事件
        public event EventHandler<CustomEventArgs> RaiseCustomEvent;

        public void DoSomething()
        {
            // 可以在这里处理一些有用的事,然后触发事件
            // 也可以在一些代码之前触发事件
            OnRaiseCustomEvent(new CustomEventArgs("Did something"));
        }

        // 把事件调用包含在一个 protected virtual 方法中,这样可以允许
        // 子类重写事件调用
        protected virtual void OnRaiseCustomEvent(CustomEventArgs e)
        {
            // 使用一个事件临时的引用,可以避免一种可能的竞争条件:
            // 如果最后一个订阅者在 null 检查之后,事件触发之前突然
            // 取消事件订阅
            EventHandler<CustomEventArgs> handler = RaiseCustomEvent;

            // 如果没有订阅者,handler 将会是 null
            if (handler != null)
            {
                // 格式化事件参数中的额外信息
                e.Message += $" at {DateTime.Now}";

                // 使用 () 操作符触发事件
                handler(this, e);
            }
        }
    }

    // 订阅者
    class Subscriber
    {
        private string id;
        public Subscriber(string ID, Publisher pub)
        {
            id = ID;
            // 使用 C# 2.0 语法订阅事件
            pub.RaiseCustomEvent += HandleCustomEvent;
        }

        // 定义事件触发时要进行的操作
        void HandleCustomEvent(object sender, CustomEventArgs e)
        {
            Console.WriteLine(id + " received this message: {0}", e.Message);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Publisher pub = new Publisher();
            Subscriber sub1 = new Subscriber("sub1", pub);
            Subscriber sub2 = new Subscriber("sub2", pub);

            // 调用触发事件的方法
            pub.DoSomething();

            // 保持控制台一直打开
            Console.WriteLine("Press Enter to close this window.");
            Console.ReadLine();

        }
    }
}    

 

posted @ 2019-10-25 11:26  sims  阅读(341)  评论(0编辑  收藏  举报