线程异步

C# 委托与事件学习笔记(小白入门版)

一、委托(Delegate)基础

1. 什么是委托?

  • 委托是 C# 中的引用类型,本质是「方法的容器」—— 可像变量一样存储、传递方法(实现 “把方法当参数用”)。

  • 核心作用:解耦代码,实现「回调机制」(如方法 A 调用方法 B)、「事件驱动」(如按钮点击响应)。

2. 委托的定义与基本使用

(1)定义委托

用 delegate 关键字声明,需明确返回值类型参数列表(即 “方法签名”,后续绑定的方法必须完全匹配)。

语法示例:

// 定义委托:返回值void,参数为1个string
delegate void MessageDelegate(string content);

(2)绑定方法并调用

委托实例可绑定「静态方法」或「实例方法」,调用委托即执行绑定的方法。

完整代码示例:

using System;

// 1. 定义委托(签名:接收string,返回void)
delegate void GreetDelegate(string name);

class GreetSystem
{
    // 2. 静态方法(无需创建对象即可调用)
    static void EnglishGreet(string name)
    {
        Console.WriteLine($"Hello, {name}!");
    }

    // 3. 实例方法(需先创建类对象)
    void ChineseGreet(string name)
    {
        Console.WriteLine($"你好,{name}!");
    }

    static void Main()
    {
        // 绑定静态方法
        GreetDelegate greet1 = EnglishGreet;
        greet1("Alice"); // 调用 → 输出:Hello, Alice!

        // 绑定实例方法(先创建对象)
        GreetSystem system = new GreetSystem();
        GreetDelegate greet2 = system.ChineseGreet;
        greet2("Bob"); // 调用 → 输出:你好,Bob!
    }
}

二、委托的实例化方式

委托是引用类型,需创建实例才能使用,有两种常见方式:

1. 显式实例化(new 关键字)

直观体现 “委托是对象”,适合新手理解原理:

// 显式创建委托实例,绑定目标方法
GreetDelegate greet = new GreetDelegate(EnglishGreet);
greet("Charlie"); // 调用 → 输出:Hello, Charlie!

2. 隐式实例化(简化写法)

编译器自动创建实例,日常开发更常用:

// 简化写法(等价于显式实例化)
GreetDelegate greet = EnglishGreet;
greet("David"); // 调用 → 输出:Hello, David!

三、多播委托(Multicast Delegate)

一个委托实例可绑定多个方法,调用时按 “添加顺序” 依次执行所有方法(用 += 添加、-= 移除)。

1. 无参数多播委托

代码示例:

using System;

// 定义无参数、无返回值的委托
delegate void NotifyDelegate();

class NotificationSystem
{
    static void SendEmail() => Console.WriteLine("✅ 发送邮件通知");
    static void SendSMS() => Console.WriteLine("✅ 发送短信通知");
    static void SendPush() => Console.WriteLine("✅ 发送推送通知");

    static void Main()
    {
        // 1. 绑定第一个方法
        NotifyDelegate notify = SendEmail;
        
        // 2. 用 += 添加其他方法
        notify += SendSMS;
        notify += SendPush;

        // 3. 调用委托(依次执行所有方法)
        Console.WriteLine("触发通知:");
        notify(); 
        // 输出:
        // ✅ 发送邮件通知
        // ✅ 发送短信通知
        // ✅ 发送推送通知

        // 4. 用 -= 移除指定方法
        notify -= SendSMS;
        Console.WriteLine("\n移除短信通知后:");
        notify(); 
        // 输出:
        // ✅ 发送邮件通知
        // ✅ 发送推送通知
    }
}

2. 带参数多播委托

核心规则:

  • 所有绑定的方法必须与委托 “参数列表完全匹配”(类型、数量、顺序一致);

  • 调用时传入的参数,会共享给所有绑定的方法

代码示例:

using System;

// 定义带1个string参数的委托
delegate void LogDelegate(string logContent);

class LogSystem
{
    static void LogToConsole(string content) => Console.WriteLine($"[控制台] {content}");
    static void LogToFile(string content) => Console.WriteLine($"[本地文件] {content}");
    static void LogToServer(string content) => Console.WriteLine($"[远程服务器] {content}");

    static void Main()
    {
        LogDelegate log = LogToConsole;
        log += LogToFile;
        log += LogToServer;

        // 传入参数,所有方法共享
        log("系统启动成功(2025-10-21 10:00)");
        // 输出:
        // [控制台] 系统启动成功(2025-10-21 10:00)
        // [本地文件] 系统启动成功(2025-10-21 10:00)
        // [远程服务器] 系统启动成功(2025-10-21 10:00)
    }
}

注意事项:

若委托有返回值,多播委托仅保留最后一个方法的返回值(前序方法的返回值会被忽略),因此多播委托通常用于 void 类型。

四、匿名方法与委托

匿名方法是 “无名称的临时方法”,直接在委托中定义,适合 “仅使用一次的简单逻辑”,减少代码冗余。

1. 匿名方法绑定委托

代码示例:

using System;

// 定义委托
delegate void PrintDelegate(string text);

class Program
{
    static void Main()
    {
        // 委托直接绑定匿名方法(用delegate关键字定义)
        PrintDelegate print = delegate(string msg)
        {
            Console.WriteLine($"匿名方法输出:{msg}");
            Console.WriteLine($"当前时间:{DateTime.Now:HH:mm:ss}");
        };

        print("测试匿名方法");
        // 输出:
        // 匿名方法输出:测试匿名方法
        // 当前时间:10:30:45(实际时间以运行时为准)
    }
}

2. Lambda 表达式(匿名方法简化版)

C# 3.0+ 推荐用 Lambda 表达式,语法更简洁(本质与匿名方法等价):

using System;

class Program
{
    static void Main()
    {
        // 1. 带参数的Lambda(匹配PrintDelegate)
        PrintDelegate print1 = msg => 
        {
            Console.WriteLine($"Lambda输出:{msg}");
        };
        print1("测试带参数Lambda"); // 输出:Lambda输出:测试带参数Lambda

        // 2. 无参数Lambda(匹配Action委托)
        Action print2 = () => Console.WriteLine("Lambda输出:无参数示例");
        print2(); // 输出:Lambda输出:无参数示例

        // 3. 带返回值的Lambda(匹配Func委托)
        Func<int, int> square = num => num * num;
        Console.WriteLine($"5的平方:{square(5)}"); // 输出:5的平方:25
    }
}

五、事件(Event)基础

事件是「委托的安全封装」,基于委托实现 “发布 - 订阅” 模式(如 UI 按钮点击、消息通知),限制外部对委托的直接修改,更安全。

1. 事件的定义与使用

完整代码示例:

using System;

// 1. 定义事件对应的委托(标准签名:sender=事件源,e=事件数据)
delegate void ButtonClickHandler(object sender, EventArgs e);

// 2. 事件发布者(如按钮控件)
class CustomButton
{
    // 定义事件(用event关键字,基于委托)
    public event ButtonClickHandler Click;

    // 触发事件的方法(通常为public,供外部调用模拟触发)
    public void OnClick()
    {
        // 先判断是否有订阅者(避免空引用异常)
        if (Click != null)
        {
            Click(this, EventArgs.Empty); // 触发事件
        }
        // 简化写法(C# 6.0+ 空合并运算符)
        // Click?.Invoke(this, EventArgs.Empty);
    }
}

// 3. 事件订阅者(如业务逻辑类)
class Program
{
    static void Main()
    {
        // 创建按钮实例(发布者)
        CustomButton btn = new CustomButton();

        // 订阅事件(用+=绑定处理方法)
        btn.Click += Btn_Click1;
        btn.Click += Btn_Click2;

        // 模拟按钮点击(触发事件)
        Console.WriteLine("点击按钮:");
        btn.OnClick();
    }

    // 事件处理方法1
    static void Btn_Click1(object sender, EventArgs e)
    {
        Console.WriteLine("处理方法1:按钮被点击,执行逻辑A");
    }

    // 事件处理方法2
    static void Btn_Click2(object sender, EventArgs e)
    {
        Console.WriteLine("处理方法2:按钮被点击,执行逻辑B");
    }
}
// 输出:
// 点击按钮:
// 处理方法1:按钮被点击,执行逻辑A
// 处理方法2:按钮被点击,执行逻辑B

2. 事件的核心特性

  • 安全限制:外部只能用 +=(订阅)和 -=(取消订阅),不能直接赋值(如 btn.Click = null)或调用(如 btn.Click()),避免委托被意外修改。

  • 发布 - 订阅分离:发布者(如按钮)负责触发事件,订阅者(如业务逻辑)负责处理事件,解耦代码。

六、学习路线总结

  1. 基础认知:委托是 “方法的引用”→ 掌握定义、绑定、调用;

  2. 进阶使用:多播委托用 +=/-= 管理多个方法 → 理解参数共享和返回值特性;

  3. 简化写法:匿名方法 / Lambda 减少代码冗余 → 熟练使用 Action/Func 预定义委托;

  4. 安全封装:事件基于委托实现发布 - 订阅 → 掌握事件的定义、订阅和触发。

按此顺序练习,结合代码示例调试,可逐步掌握委托与事件的核心用法!

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容