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日 11:46
下一篇 2021年2月7日 17:15

相关推荐

  • 2.10C#函数成员之终结器(Finalizer)

    终结器声明 终结器(Finalizer),以前称为 析构函数(Destructor),用于在类实例被垃圾回收器回收时执行任何必要的最终清理操作。因此,无法在结构中定义终结器,它们仅用于类。 终结器的声明方式与方法相似,名称为所属类的名称前加~波浪号,不过既不能包含参数和可访问性修饰符,也不能进行显式调用。 重载 终结器…

    C#语言教程 2021年2月10日
    03220
  • 2.1C#语言的变量(Variable)

    1.变量声明和初始化 2.变量分类 x 是一个静态变量, y 它是一个实例变量,a 是一个值参数,b 是一个引用参数,c 是一个输出参数,v[0] 是一个数组元素,i 是一个局部变量。 2.1成员变量 名称 修饰符 静态变量 用 static 修饰 实例变量 无 static 修饰 2.2…

    C#语言教程 2021年2月1日
    02750
  • 3.9C#引用类型之特性(Attribute)

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

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

发表评论

登录后才能评论