首页 > 编程语言 >【C#】委托与事件——3.EventHandler

【C#】委托与事件——3.EventHandler

时间:2024-12-28 14:31:27浏览次数:6  
标签:EventHandler 订阅 委托 C# void 实例 事件 public

在 C# 中,EventHandler 是一种特殊的委托类型,专门用于事件处理。它定义在 System 命名空间中,并且通常用来实现发布-订阅模式,这是 .NET 框架中处理事件的标准方式。

EventHandler 的定义如下:

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

这里有几个关键点需要注意:

  1. sender 参数:这是一个 object 类型的参数,代表触发事件的对象。这通常是发出事件的那个对象实例(比如一个按钮控件)。

  2. EventArgs 参数:这是一个 EventArgs 类型的参数,提供了与事件相关的数据。对于不需要传递额外信息的基本事件,可以使用这个基类。如果需要携带更多信息,则可以继承自 EventArgs 创建特定的事件参数类。

使用示例

下面是一个简单的例子,展示如何使用 EventHandler 来定义和处理事件:

using System;

// 定义一个自定义的EventArgs类来携带额外的数据
public class MyEventArgs : EventArgs
{
    public string Message { get; set; }

    public MyEventArgs(string message)
    {
        Message = message;
    }
}

// 一个类,包含一个事件
public class EventPublisher
{
    // 定义事件
    public event EventHandler<MyEventArgs> MyEvent;

    // 触发事件的方法
    protected virtual void OnMyEvent(MyEventArgs e)
    {
        MyEvent?.Invoke(this, e);
    }

    // 产生事件的动作
    public void DoSomething()
    {
        Console.WriteLine("Doing something...");
        // 在某些条件下触发事件
        OnMyEvent(new MyEventArgs("Something happened!"));
    }
}

class Program
{
    static void Main()
    {
        var publisher = new EventPublisher();

        // 订阅事件
        publisher.MyEvent += (sender, e) => 
            Console.WriteLine($"Event received: {e.Message}");

        // 执行操作,可能会触发事件
        publisher.DoSomething();
    }
}

在这个例子中:

  • 我们定义了一个 MyEventArgs 类来携带事件数据。
  • EventPublisher 类有一个 MyEvent 事件,当调用 DoSomething 方法时,该事件会被触发。
  • 在主程序中,我们创建了 EventPublisher 的一个实例,并为 MyEvent 事件添加了一个处理器(lambda 表达式),该处理器将在事件被触发时打印一条消息。

通过这种方式,EventHandler 和它的泛型版本 EventHandler<TEventArgs> 提供了一种标准化的方式来处理事件,使得组件之间的通信更加松散耦合。


通过显示委托将事件处理方法订阅到事件(Handler常用)

在实际使用中,我们可以先创建一个委托实例(evenHandler),并让它指向某个方法(事件处理方法),然后再将这个委托实例添加到事件的订阅中。这种方法也很常见,尤其是在以下几种场景下:

  1. 需要在多个地方使用同一个委托实例: 如果你需要在多个地方重用同一个委托实例,创建一个委托实例并绑定到事件是一个常见的做法。这样,你可以控制委托的生命周期,并且可以在多个事件中使用相同的处理方法。

  2. 事件订阅时需要更多的控制: 在某些情况下,可能需要为事件处理程序提供更多的控制,例如传递额外的参数或对方法进行封装。此时,通过创建一个委托实例并将其绑定到事件,可以提供更灵活的控制。

让我们通过一个实际的例子来进一步分析:

1. 通过创建委托实例并将其指向方法来订阅事件

public class MyClass
{
    // 定义委托类型
    public delegate void MyEventHandler(string message);

    // 声明事件
    public event MyEventHandler MyEvent;

    public void TriggerEvent(string message)
    {
        // 触发事件
        MyEvent?.Invoke(message);
    }
}

public class Program
{
    public static void Main()
    {
        MyClass obj = new MyClass();

        // 创建委托实例并将其指向事件处理方法
        MyClass.MyEventHandler eventHandler = new MyClass.MyEventHandler(MyEventHandlerMethod);

        // 将委托实例订阅到事件
        obj.MyEvent += eventHandler;

        // 触发事件
        obj.TriggerEvent("Hello, world!");
    }

    // 事件处理方法
    public static void MyEventHandlerMethod(string message)
    {
        Console.WriteLine($"Event triggered: {message}");
    }
}

在这个例子中,MyClass.MyEventHandler 是我们定义的委托类型。我们首先创建了一个委托实例 eventHandler,并将它指向 MyEventHandlerMethod 这个方法。然后我们使用 += 操作符将 eventHandler 委托实例添加到 obj.MyEvent 事件中。最后,我们触发事件时,eventHandler 会执行 MyEventHandlerMethod

2. 委托实例与直接通过 += 订阅事件的区别

通过委托实例和直接订阅事件(通过 +=)本质上是等效的。只是通过委托实例订阅事件提供了更多的灵活性和控制,尤其在以下情况下:

  • 控制委托实例的生命周期:如果你需要管理委托实例,尤其是在多次订阅同一事件的场景下,使用委托实例更加方便。
  • 对事件处理进行更复杂的封装或管理:通过委托实例,你可以为方法添加一些逻辑,比如通过 new MyDelegate(MyEventHandlerMethod) 将方法封装在特定的委托类型下,或者使用匿名方法或 lambda 表达式。

3. 直接通过 += 订阅事件(简化形式)

这是一种更简单和常见的方式:

public class MyClass
{
    // 定义委托类型
    public delegate void MyEventHandler(string message);

    // 声明事件
    public event MyEventHandler MyEvent;

    public void TriggerEvent(string message)
    {
        // 触发事件
        MyEvent?.Invoke(message);
    }
}

public class Program
{
    public static void Main()
    {
        MyClass obj = new MyClass();

        // 直接通过 += 订阅事件处理方法
        obj.MyEvent += MyEventHandlerMethod;

        // 触发事件
        obj.TriggerEvent("Hello, world!");
    }

    // 事件处理方法
    public static void MyEventHandlerMethod(string message)
    {
        Console.WriteLine($"Event triggered: {message}");
    }
}

在这种方式中,我们直接通过 += 订阅事件,无需显式创建委托实例。这种方式非常简洁,适用于大多数情况。

4. 总结

  • 通过委托实例订阅事件:这种方法允许你显式地控制委托实例,尤其在你需要重用同一个委托实例、封装委托或对委托实例有更多控制时是非常有用的。你首先创建委托实例,然后将它与事件关联。

  • 直接通过 += 订阅事件:这种方式是最常见的,简洁且方便。它不需要显式地创建委托实例,直接通过事件处理方法来订阅。

这两种方式本质上是等效的,选择使用哪种方式取决于具体的需求和场景。

这样做允许开发者将逻辑(在这个例子中是对到达的数据包进行处理)与事件触发分离,使得代码更加模块化和易于管理。

 

为什么要使用显示的委托而不是直接绑定方法

尽管直接绑定方法到事件是简洁和直观的方式,但在某些情况下使用显式的委托变量(如你的例子中的arrivalEventHandler)可以带来一些好处:

  1. 可重用性:如果你需要多次订阅或取消订阅同一个事件处理程序,使用委托变量可以使代码更清晰。例如,你可以轻松地从多个地方引用相同的委托实例。

  2. 解耦合:通过委托变量,你可以更容易地在运行时改变事件处理器。比如,你可以在某个条件满足时更换事件处理器,而不需要重新设置整个事件订阅。

  3. 取消订阅方便:如果你想在某个时刻取消对事件的订阅,拥有委托变量会使这个过程更加明确和简单。例如:

    device.OnPacketArrival -= arrivalEventHandler;
  4. 调试和维护:使用显式的委托可以让代码更加易于理解和维护。特别是当事件处理器逻辑比较复杂时,可以通过委托变量更好地组织代码。

  5. 传递额外状态:如果你需要向事件处理器传递额外的状态信息,可以通过委托闭包来实现。虽然这不是你当前示例的情况,但它展示了委托的一种强大功能。

 

标签:EventHandler,订阅,委托,C#,void,实例,事件,public
From: https://www.cnblogs.com/ban-boi-making-dinner/p/18637469

相关文章

  • Navicat密码导出解密导入到DataGrip中
    使用Navicat导出密码:目前使用Navicat17亲测有效使用php解密代码<?phpclassNavicatPassword{protected$version=0;protected$aesKey='libcckeylibcckey';protected$aesIv='libccivlibcciv';protected$blowString='3DC5CA39&#......
  • Codeforces Round 993 (Div. 4)
    Codeforces题解-[题目名称]题目链接题目描述Wave获得了五个整数$k$、$l_1$、$r_1$、$l_2$和$r_2$。Wave希望你帮助她计算出有序对$(x,y)$的数量,使得以下所有条件都得到满足:$l_1\leqx\leqr_1$。$l_2\leqy\leqr_2$。存在一个非负整数$n$使得......
  • 大模型入门书籍丨ChatGLM3大模型本地化部署、应用开发与微调(附PDF)
    这里给大家推荐一本大模型书籍《ChatGLM3大模型本地化部署、应用开发与微调》。这本书适合大模型的初学者、有一定基础的大模型研究人员、大模型应用开发人员。同时,还可作为高等院校或高职高专相关专业大模型课程的教材,助力培养新一代的大模型领域人才。《ChatGLM3大模型......
  • 性能优化利器❕ - 深入浅出IntersectionObserver系列
    这IntersectionObserverIntersectionObserver基本用法1、创建观察器2、观察目标元素3、停止观察:4、配置项图片懒加载拓展:无限滚动首屏动画优化好处IntersectionObserverIntersectionObserver(交叉观察器)是浏览器提供的一种API,用于检测一个元素是否出现在视口(viewpor......
  • Omnissa Horizon Clients 2412 发布 - 虚拟桌面基础架构 (VDI) 和应用软件
    OmnissaHorizonClients2412发布-虚拟桌面基础架构(VDI)和应用软件OmnissaHorizon,之前称为VMwareHorizon,通过高效、安全的虚拟桌面交付增强您的工作空间请访问原文链接:https://sysin.org/blog/omnissa-horizon-8/查看最新版。原创作品,转载请保留出处。作者主页:sys......
  • C++ 中将 float 类型转换为 std::string
    在C++中,可以使用多种方法将 float 类型转换为 std::string 类型。以下是常用的几种方法:方法1:std::to_string (C++11及以上)这是最简单的方法之一,直接使用 std::to_string。#include<iostream>#include<string>intmain(){floatnum=123.456f;std::......
  • C++高级程序设计 20241228
    当然可以。在C++中,面向对象编程(OOP)是一种编程范式,它使用类和对象来模拟现实世界中的实体和行为。以下是构造函数、拷贝构造函数、析构函数和普通成员函数的简单解释和例子:1.构造函数构造函数是一种特殊的成员函数,用于创建对象时初始化对象的状态。它与类同名,并且没有返回类型,甚......
  • 【C++】异常
      ......
  • Javascript数据结构常见题目(一)
    以下是每个问题的JavaScript实现:1.下一个更大元素(循环数组)functionnextGreaterElements(nums){letn=nums.length;letresult=Array(n).fill(-1);letstack=[];for(leti=0;i<2*n;i++){letnum=nums[i%n];......
  • Javascript数据结构常见面试题目(全)
    以下是一个前端JavaScript数据结构常见题目框架,可以帮助你快速组织思路并解决问题:框架内容1.数组相关查找与排序:寻找数组的最大/最小值。快速排序、归并排序、冒泡排序。操作:移除重复项:newSet()或双指针法。滑动窗口法:求最大/最小子数组和。二分查找:查找有序数......