2.7C#函数成员之事件(Event)

1.事件委托声明

.NET 类库中的所有事件均基于 EventHandler 委托、EventHandler<TEventArgs> 委托,一般不建议自定义委托。

//未生成事件数据的委托
public delegate void EventHandler(object? sender, EventArgs e);

//生成事件数据的委托
public delegate void EventHandler<TEventArgs>(object? sender, TEventArgs e);

//自定义委托
public delegate void CustomEventHandler(object sender, CustomEventArgs args);

第一个参数为object类型,表示能够引发事件的类的实例。

第二个参数为从 EventArgs 基类派生的子类型的实例。如果事件不生成事件数据,第二个参数就是 EventArgs.Empty 字段的值。如果事件生成事件数据,则提供保存事件数据所需的任何字段或属性。

2.事件声明

事件的声明方式与字段相似,区别是事件声明包括 event 关键字,且类型必须是委托类型。

event 委托类型 事件名;
//EventHandler委托类型
public event EventHandler RaiseCustomEvent;

//EventHandler<CustomEventArgs>委托类型
public event EventHandler<CustomEventArgs> RaiseCustomEvent;

//自定义委托类型
public event CustomEventHandler RaiseCustomEvent;

3.订阅事件和取消订阅

通过使用 += 运算符订阅事件,使用 -= 运算符取消订阅。

pub.RaiseCustomEvent += HandleCustomEvent;
pub.RaiseCustomEvent -= HandleCustomEvent;

4.事件访问器

实际上,目标方法是通过事件访问器添加到委托的调用列表中,事件访问器类似于属性访问器,不同之处在于事件访问器命名为 add 和 remove。 在大多数情况下,无需提供自定义事件访问器。 如果代码中没有提供自定义事件访问器,编译器将自动添加它们。 但在某些情况下,编译器不自动添加它们,可能需要我们自己提供自定义行为,比如:类继承自两个或多个接口,且每个接口都具有相同名称的事件。 在这种情况下,我们必须为至少其中一个事件提供显式接口实现。为事件编写显式接口实现时,还必须编写 add 和 remove 事件访问器。

event EventHandler IDrawingObject.OnDraw
{
    add
    {
        lock (objectLock)
        {
            PreDrawEvent += value;
        }
    }
    remove
    {
        lock (objectLock)
        {
            PreDrawEvent -= value;
        }
    }
}

5.完整示例

using System;

namespace DotNetEvents
{
    // Define a class to hold custom event info
    public class CustomEventArgs : EventArgs
    {
        public CustomEventArgs(string message)
        {
            Message = message;
        }

        public string Message { get; set; }
    }

    // Class that publishes an event
    class Publisher
    {
        // Declare the event using EventHandler<T>
        public event EventHandler<CustomEventArgs> RaiseCustomEvent;

        public void DoSomething()
        {
            // Write some code that does something useful here
            // then raise the event. You can also raise an event
            // before you execute a block of code.
            OnRaiseCustomEvent(new CustomEventArgs("Event triggered"));
        }

        // Wrap event invocations inside a protected virtual method
        // to allow derived classes to override the event invocation behavior
        protected virtual void OnRaiseCustomEvent(CustomEventArgs e)
        {
            // Make a temporary copy of the event to avoid possibility of
            // a race condition if the last subscriber unsubscribes
            // immediately after the null check and before the event is raised.
            EventHandler<CustomEventArgs> raiseEvent = RaiseCustomEvent;

            // Event will be null if there are no subscribers
            if (raiseEvent != null)
            {
                // Format the string to send inside the CustomEventArgs parameter
                e.Message += $" at {DateTime.Now}";

                // Call to raise the event.
                raiseEvent(this, e);
            }
        }
    }

    //Class that subscribes to an event
    class Subscriber
    {
        private readonly string _id;

        public Subscriber(string id, Publisher pub)
        {
            _id = id;

            // Subscribe to the event
            pub.RaiseCustomEvent += HandleCustomEvent;
        }

        // Define what actions to take when the event is raised.
        void HandleCustomEvent(object sender, CustomEventArgs e)
        {
            Console.WriteLine($"{_id} received this message: {e.Message}");
        }
    }

    class Program
    {
        static void Main()
        {
            var pub = new Publisher();
            var sub1 = new Subscriber("sub1", pub);
            var sub2 = new Subscriber("sub2", pub);

            // Call the method that raises the event.
            pub.DoSomething();

            // Keep the console window open
            Console.WriteLine("Press any key to continue...");
            Console.ReadLine();
        }
    }
}

6.修饰符

事件支持 abstract、new、override、sealed、static、virtual 修饰符。

原创文章,作者:huoxiaoqiang,如若转载,请注明出处:https://www.huoxiaoqiang.com/csharp/csharplang/8221.html

(0)
上一篇 2021年2月6日 03:18
下一篇 2021年2月7日 20:00

相关推荐

  • 3.7C#引用类型之数组(Array)

    数组是一种数据结构。 1.一维数组 如果在不初始化的情况下声明一维数组变量,则必须使用 new 运算符将数组赋予变量。  2.多维数组 多维数组是元素类型是一维数组的数组。 多维数组的维度 是 1 与 方括号内的逗号数量相加的结果。 如果在不初始化的情况下声明多维数组变量,则必须使用&nb…

    C#语言教程 2021年3月7日
    06740
  • 3.5C#引用类型之接口(Interface)

    1.接口声明 接口包含一组非抽象class或struct必须实现的相关功能的定义。 2.继承 接口 可以继承自多个基接口。 3.实现 3.1实现 类 或 结构 可以实现多个接口,实现接口的类或结构必须实现其所有未提供默认实现的成员并匹配接口中的成员签名,非必须重写实现具有默认实现的成员。 3.2默认实现 接口…

    C#语言教程 2021年3月5日
    06840
  • 3.9C#引用类型之特性(Attribute)

    1.特性声明 所有特性类都直接或间接派生自 System.Attribute 基类。 以下示例声明了 HelpAttribute 特性。 2.调用特性 调用特性时,通过用方括号 ([]) 将特性名称以及实参括起来,并置于应用该特性的实体的声明上方。可以将多个特性附加到实体的声明上。对于给定实体,一些…

    C#语言教程 2021年3月9日
    02800

发表回复

登录后才能评论