C#:事件

事件介绍


和 委托 类似,事件是后期绑定机制。 实际上,事件是建立在对 委托 的语言支持之上的。

事件是对象用于(向系统中的所有相关组件)广播已发生事情的一种方式。 任何其他组件都可以订阅事件,并在事件引发时得到通知。

许多图形系统都具有用于报告用户交互的事件模型。 这些事件会报告鼠标移动、按钮点击和类似的交互。 这是使用事件的最常见情景之一,但并非唯一的情景。

要使用事件首先就得定义委托,如下:

// 定义委托
public delegate void delegateTest();

然后再定义使用 event 关键字的事件:

// 定义事务
public event delegateTest eventTest;

对事件所依赖的事务赋值:

// 事件赋值
Program program = new Program();
program.eventTest = () => Console.WriteLine("我是事务中的方法");

调用事件:

// 调用事件
if(program.eventTest != null)
    program.eventTest();

// 或者
program.eventTest.Invoke();

通过使用 += 运算符增加订阅事件:

static void addMethod()
{
    Console.WriteLine("我是增加的方法");
}

program.eventTest += addMethod;

使用 -= 运算符取消订阅:

program.eventTest -= addMethod;

请务必为表示事件处理程序的表达式声明局部变量。 这将确保取消订阅删除该处理程序。 如果使用的是 lambda 表达式的主体,则将尝试删除从未附加过的处理程序,此操作为无效操作。

完整示例:

class Program
{
    // 定义委托
    public delegate void delegateTest();

    // 定义事件
    public event delegateTest eventTest;

    static void addMethod()
    {
        Console.WriteLine("我是增加的方法");
    }

    static void Main(string[] args)
    {
        // 事件赋值
        Program program = new Program();
        program.eventTest = () => Console.WriteLine("我是事务中的方法");
        program.eventTest += addMethod;
        program.eventTest -= addMethod;

        // 调用时间
        if(program.eventTest != null)
            program.eventTest();
        // 或者
        program.eventTest?.Invoke();
    }
}

标准.Net事件模式


.Net还内置了两个常用的委托类型 EventHandler 和  EventHandler

通常,任何事件都应包含两个参数:事件源和事件数据。

EventHandler 主要用于不包含数据,对包含数据的使用 EventHandler

对 .Net Core 中定义 EventHandler 和 EventHandler 感兴趣的课查阅 .Net Core EventHandler 。

.Net 事件委托标准签名

在了解 .Net 内置委托值之前,我们先来了解一下 .Net 事件委托的标准签名:

void OnEventRaised(object sender, EventArgs args);

返回类型为 void。事件基于委托,而且是多播委托。对任何事件源都支持多个订阅服务器。 来自方法的单个返回值不会扩展到多个事件订阅服务器。

参数列表包含两种参数:发件人和事件参数。

  1. sender 的编译时类型为 System.Object,即使有一个始终正确的更底层派生的类型亦是如此。 按照惯例使用 object
  2. 第二种参数通常是派生自 System.EventArgs 的类型。

如果事件类型不需要任何其他参数,你仍将提供这两个参数。 应使用特殊值 EventArgs.Empty 来表示事件不包含任何附加信息。

EventHandler 委托

示例:

class Program
{
    static void Main(string[] args)
    {
        EventSample sample = new EventSample();
        sample.ProcessCompeleted += Sample_ProcessCompeleted;
        sample.Start();
    }

    // EventHandler 委托固定参数sender,args
    private static void Sample_ProcessCompeleted(object sender, EventArgs args) => Console.WriteLine("处理完成");
}

class EventSample
{
    // 事件
    public event EventHandler ProcessCompeleted;

    public void Start()
    {
        Console.WriteLine("开始作业");

        // 业务逻辑

        // 处理完毕后触发事件
        OnProcessCompeleted(EventArgs.Empty);
    }

    // 触发事件
    protected virtual void OnProcessCompeleted(EventArgs args) => ProcessCompeleted?.Invoke(this, args);
}

上面代码中,Sample_ProcessCompeleted() 方法多了两个与 EventHandler 匹配的参数。此外,当我们在方法中使用引发事件时,this 作为发送者传递。另外,我们的事件不需要数据,所以使用 EventArgs.Empty 。

传递事件数据

大多数事件都会向订阅者发送一些数据。EventArgs 类是所有事件数据的基类。.NET 还包含了其它的内置事件数据类,如:SerialDataReceivedEventArgs 。它遵循以 EventArgs 结尾的命名方式。我们可以通过继承 EventArgs 来自定义事件数据。
示例:

public class ProcessCompeletedEventArgs : EventArgs
{
    public bool IsCompeleted { get; set; }
 
    /// 
    /// 完成时间
    /// 
    public DateTime CompeletedAt { get; set; }
}
 
public class EventSample
{
    //public delegate void Notify();
 
    public event EventHandler ProcessCompeleted;
 
    public void Start()
    {
        Console.WriteLine("开始作业");
        //业务逻辑
 
        ProcessCompeletedEventArgs e = new ProcessCompeletedEventArgs
        {
            IsCompeleted = true,
            CompeletedAt =DateTime.Now
        };
 
        //处理完毕后触发事件
        OnProcessCompeleted(e);
    }
 
    protected virtual void OnProcessCompeleted(ProcessCompeletedEventArgs e)
    {
        ProcessCompeleted?.Invoke(this, e);
    }
}
class Program
{
    static void Main(string[] args)
    {
        EventSample sample = new EventSample();
        sample.ProcessCompeleted += Sample_ProcessCompeleted;
        sample.Start();
 
        Console.ReadKey();
    }
 
    private static void Sample_ProcessCompeleted(object sender, ProcessCompeletedEventArgs e)
    {
        Console.WriteLine("IsCompeleted:{0},CompletedAt:{1}", e.IsCompeleted, e.CompeletedAt);
 
        Console.WriteLine("处理完成");
    }
}

区别委托和事件


相同点

  • 都提供了一个后期绑定方案:在该方案中,组件通过调用仅在运行时识别的方法进行通信。
  • 都支持单个和多个订阅服务器方法。这称为单播和多播支持。
  • 二者均支持用于添加和删除处理程序的类似语法
  • 发事件和调用委托使用完全相同的方法调用语法。它们甚至都支持与 ?. 运算符一起使用的相同的 Invoke() 方法语法。

不同点

  • 事件具有专用调用。包含事件的类以外的类只能添加和删除事件侦听器;只有包含事件的类才能调用事件。 事件通常是公共类成员。 相比之下,委托通常作为参数传递,并存储为私有类成员(如果它们全部存储)。
  • 事件侦听器通常具有较长的生存期。事件侦听器通常具有较长的生存期的这一理由不太充分。 但是,你可能会发现,当事件源将在很长一段时间内引发事件时,基于事件的设计会更加自然。

如何选择

  • 在确定要使用的语言功能时,最重要的考虑因素为:是否必须具有附加的订阅服务器。 如果代码必须调用订阅服务器提供的代码,则在需要实现回调时,应使用基于委托的设计。 如果你的代码在不调用任何订阅服务器的情况下可完成其所有工作,则应使用基于事件的设计。
  • 另一个注意事项是委托方法所需的方法原型。如你所见,用于事件的委托均具有无效的返回类型。你还看到,存在创建事件处理程序的惯用语,该事件处理程序通过修改事件参数对象的属性将信息传回到事件源。虽然这些惯用语可发挥作用,但它们不像从方法返回值那样自然。请注意,这两种试探法可能经常同时存在:如果委托方法返回值,则可能会以某种方式影响算法。

参考:

        Microsoft C# 事件

你可能感兴趣的:(C#,c#,开发语言)