C# 中的委托(Delegate)与事件(Event)

一、委托(Delegate):类型安全的 "函数指针"

1. 核心概念

委托是 C# 中的一种引用类型,它的作用是 "封装" 一个或多个方法(方法的签名和返回值必须匹配),可以把它理解为一个类型安全的函数指针。你可以把方法当作参数传递、存储或调用,这是实现回调、解耦代码的基础。

2. 基本用法(完整代码示例)
using System;

// 1. 定义委托:确定要封装的方法的签名(返回值+参数)
public delegate void GreetingDelegate(string name);

class Program
{
    // 2. 定义匹配委托签名的方法
    static void ChineseGreeting(string name)
    {
        Console.WriteLine($"你好,{name}!");
    }

    static void EnglishGreeting(string name)
    {
        Console.WriteLine($"Hello, {name}!");
    }

    static void Main(string[] args)
    {
        // 3. 创建委托实例并绑定方法
        GreetingDelegate greet1 = ChineseGreeting;
        // 调用委托(本质是调用绑定的方法)
        greet1("张三"); // 输出:你好,张三!

        // 4. 委托的多播(绑定多个方法)
        GreetingDelegate greetAll = ChineseGreeting;
        greetAll += EnglishGreeting; // += 添加方法
        greetAll("李四"); // 依次调用两个方法,输出:你好,李四! Hello, 李四!

        // 5. 移除委托中的方法
        greetAll -= ChineseGreeting;
        greetAll("王五"); // 仅输出:Hello, 王五!
    }
}
3. 关键解释
  • 委托定义public delegate void GreetingDelegate(string name); 规定了委托能封装的方法必须是:无返回值、参数为 1 个 string 类型。
  • 委托实例化:直接将方法名赋值给委托变量(无需括号),因为委托本质是封装方法。
  • 多播委托:通过+=添加方法、-=移除方法,调用时会按添加顺序执行所有绑定的方法(如果有返回值,仅返回最后一个方法的结果)。
  • 常用内置委托:C# 提供了Action<T>(无返回值)、Func<T,TResult>(有返回值)等内置委托,无需手动定义:
    // 使用内置Action委托替代自定义GreetingDelegate
    Action<string> greet = ChineseGreeting;
    greet("赵六"); // 输出:你好,赵六!

    二、事件(Event):委托的 "安全封装"

    1. 核心概念

    事件是基于委托的一种特殊机制,它限制了委托的访问权限:外部类只能通过+=订阅事件、-=取消订阅,无法直接调用委托或用=覆盖委托(避免意外修改),是实现 "发布 - 订阅" 模式的核心。

    2. 基本用法(完整代码示例:发布 - 订阅模式)
    using System;
    
    // 发布者类:触发事件的一方
    public class AlarmClock
    {
        // 1. 定义委托(事件的"签名")
        public delegate void RingEventHandler(object sender, EventArgs e);
    
        // 2. 定义事件:基于上面的委托
        public event RingEventHandler Ring;
    
        // 3. 触发事件的方法(通常是protected virtual,供子类重写)
        protected virtual void OnRing()
        {
            // 先判断是否有订阅者,避免空引用异常
            Ring?.Invoke(this, EventArgs.Empty);
        }
    
        // 模拟闹钟响铃
        public void Start(int seconds)
        {
            Console.WriteLine($"闹钟开始计时,{seconds}秒后响铃...");
            System.Threading.Thread.Sleep(seconds * 1000); // 模拟等待
            OnRing(); // 触发事件
        }
    }
    
    // 订阅者类1:响应事件的一方
    public class Person
    {
        private string _name;
    
        public Person(string name)
        {
            _name = name;
        }
    
        // 订阅事件的方法(签名必须匹配委托)
        public void WakeUp(object sender, EventArgs e)
        {
            Console.WriteLine($"{_name}:听到闹钟响,起床啦!");
        }
    }
    
    // 订阅者类2
    public class Cat
    {
        public void React(object sender, EventArgs e)
        {
            Console.WriteLine("小猫:被闹钟吵醒,喵喵叫!");
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            // 1. 创建发布者(闹钟)
            AlarmClock clock = new AlarmClock();
    
            // 2. 创建订阅者
            Person tom = new Person("Tom");
            Cat kitty = new Cat();
    
            // 3. 订阅事件:将订阅者的方法绑定到事件
            clock.Ring += tom.WakeUp;
            clock.Ring += kitty.React;
    
            // 4. 启动闹钟(触发事件)
            clock.Start(2);
    
            // 5. 取消订阅
            clock.Ring -= kitty.React;
            Console.WriteLine("\n取消小猫的订阅后,再次响铃:");
            clock.Start(1);
        }
    }
    3. 输出结果
    闹钟开始计时,2秒后响铃...
    Tom:听到闹钟响,起床啦!
    小猫:被闹钟吵醒,喵喵叫!
    
    取消小猫的订阅后,再次响铃:
    闹钟开始计时,1秒后响铃...
    Tom:听到闹钟响,起床啦!
    4. 关键解释
  • 事件定义public event RingEventHandler Ring; 事件必须基于委托定义,且通常放在类内部。
  • 触发事件:通过OnXXX()方法触发(命名规范:On + 事件名),用Ring?.Invoke()避免空引用(没有订阅者时委托为 null)。
  • 访问限制:外部类无法直接调用clock.Ring()clock.Ring = tom.WakeUp,只能用+=/-=,保证了代码安全性。
  • 标准事件参数:实际开发中常用EventHandler<TEventArgs>内置委托,无需自定义委托:
    // 替换自定义委托,使用内置EventHandler
    public event EventHandler Ring;
    // 触发逻辑不变:Ring?.Invoke(this, EventArgs.Empty);

    三、委托与事件的核心区别

    特性委托(Delegate)事件(Event)
    访问权限外部可直接调用、覆盖外部仅能订阅 / 取消订阅
    本质引用类型(函数指针)委托的封装(特殊语法)
    适用场景灵活的方法封装、回调发布 - 订阅模式、解耦组件
    多播支持支持(+=/-=)支持(底层依赖委托多播)

    总结

  • 委托是封装方法的引用类型,可多播,是事件的基础,核心作用是将方法作为参数传递 / 调用。
  • 事件是委托的安全封装,限制了外部对委托的操作,仅开放订阅 / 取消订阅,是实现发布 - 订阅模式的标准方式。
  • 实际开发中优先使用Action<T>/Func<T,TResult>(内置委托)和EventHandler<T>(事件委托),减少自定义委托的代码量。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

工业程序猿老赵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值