C#进阶学习(九)委托的介绍

目录

一、什么是委托?

二、委托的作用

三、委托的基本声明规则

[delegate delegate delegate](#delegate delegate delegate)

四、委托的特点及用法

[多播委托(Multicast Delegate)](#多播委托(Multicast Delegate))

动态切换方法逻辑

将方法作为参数传递

五、系统给我们提供好的委托

[1、Action 委托](#1、Action 委托)

[表示无返回值的方法,支持 0~16 个参数。](#表示无返回值的方法,支持 0~16 个参数。)

[示例 1:无参数](#示例 1:无参数)

[示例 2:带参数](#示例 2:带参数)

[2、Func 委托](#2、Func 委托)

表示有返回值的方法,最后一个泛型参数为返回类型

[示例 1:无参数有返回值](#示例 1:无参数有返回值)

[示例 2:带参数有返回值](#示例 2:带参数有返回值)

3、Predicate 委托

专用于返回布尔值的方法,常见于集合操作。

[示例 1:筛选整数集合](#示例 1:筛选整数集合)

[示例 2:筛选字符串集合](#示例 2:筛选字符串集合)

六、总结


一、什么是委托?

定义:委托(Delegate) 是 C# 中一种类型安全的函数指针机制,本质是一个类(继承自 System.MulticastDelegate),用于封装一个或多个具有相同签名(参数和返回值类型)的方法。委托的核心作用是将方法作为参数传递,或动态调用多个方法,实现回调机制和事件驱动编程。

关键点

  • 委托是类型安全的,编译器会检查方法签名是否匹配。

  • 委托可以指向静态方法或实例方法。

  • 委托是面向对象的,比 C/C++ 的函数指针更安全。

叽里咕噜说啥呢,看不懂。说人话就是,一个函数变量, 你可以把他理解为一个函数的容器,就像你申明一个int类型的数据,这个数据装载着一个整型数据,委托也是一样,只不过装的是函数。

委托是 函数的容器
可以理解为表示函数的变量类型
用来存储传递函数的方法
委托的本质是一个类 用来定义函数的类型(返回值和参数的类型)
不同的 函数 必须对应各自"格式"一致的委托

二、委托的作用

  1. 解耦调用方与被调用方

    调用方无需直接依赖具体方法,只需通过委托调用,提升代码灵活性。

  2. 实现回调机制

    例如异步编程中,方法完成后通过委托通知调用方。

  3. 事件处理的基础

    事件(Event)本质是委托的封装,用于实现观察者模式。这点我们后面会学到的,今天先了解。

  4. 多播(Multicast)能力

    一个委托可以绑定多个方法,按顺序执行。

  5. 支持 Lambda 表达式和匿名方法

    简化代码,尤其在 LINQ 和异步编程中广泛应用。这是一个很方便的方式写函数,我们后面学习。

三、委托的基本声明规则

请务必记住一个关键字

delegate delegate delegate

委托的声明语法 其实就是在函数前面加了一个关键字同时去除了函数体

关键字:delegate
语法:访问修饰符 delegate 返回值类型 委托名称(参数列表);

写在哪里
可以申明在namespace中也可以写在class中
更多的写在namespace中

简单的记忆委托语法 就是:函数申明语法前面加一个delegate关键字

委托的定义
访问修饰符不写 默认是public 在别的命名空间中也可以访问
private 别的空间就不能用了
一般用public

cs 复制代码
delegate [返回类型] DelegateName([参数列表]);

例如:

cs 复制代码
delegate int Calculate(int a, int b);

方法签名必须匹配

委托指向的方法必须与委托的参数类型、顺序、数量返回类型完全一致。

实例化委托

必须通过 new 关键字或直接赋值方法名初始化:

cs 复制代码
Calculate calc1 = new Calculate(Add); // 传统方式
Calculate calc2 = Subtract;          // C# 2.0 简化语法

static int Add(int x, int y) => x + y;
static int Subtract(int x, int y) => x - y;

一些申明示例:

下面提到的必须静态 指的是 因为你要在主函数中使用,所以必须静态

无参数无返回值的委托

cs 复制代码
using System;

// 1. 声明委托类型
delegate void SimpleDelegate();

public class Program {
    // 2. 定义符合委托签名的方法(必须静态)
    static void SayHello() {
        Console.WriteLine("Hello!");
    }

    static void Main() {
        // 3. 创建委托实例并绑定方法
        SimpleDelegate greet = SayHello;

        // 4. 调用委托
        greet(); // 输出 "Hello!"
    }
}

有参数无返回值的委托

cs 复制代码
using System;

// 声明委托
delegate void ParameterDelegate(int a, string b);

public class Program {
    // 定义方法(必须静态)
    static void ShowInfo(int id, string name) {
        Console.WriteLine($"ID: {id}, Name: {name}");
    }

    static void Main() {
        // 创建委托实例并调用
        ParameterDelegate print = ShowInfo;
        print(101, "Alice");  // 输出:ID: 101, Name: Alice
    }
}

无参数有返回值的委托

cs 复制代码
using System;

// 声明委托
delegate int ReturnValueDelegate();

public class Program {
    // 定义方法(必须静态)
    static int GenerateRandom() {
        return new Random().Next(1, 100);
    }

    static void Main() {
        ReturnValueDelegate getNumber = GenerateRandom;
        int num = getNumber();
        Console.WriteLine($"随机数: {num}"); // 输出:随机数: 42(示例值)
    }
}

有参数有返回值的委托

cs 复制代码
using System;

// 声明委托
delegate bool LogicDelegate(int x, int y);

public class Program {
    // 定义方法(必须静态)
    static bool IsEqual(int a, int b) {
        return a == b;
    }

    static void Main() {
        LogicDelegate check = IsEqual;
        bool result = check(5, 5);
        Console.WriteLine($"是否相等: {result}"); // 输出:是否相等: True
    }
}

单个泛型的委托

cs 复制代码
using System;

// 声明泛型委托
delegate T GenericDelegate<T>();

public class Program {
    // 定义方法(必须静态)
    static int ReturnInt() => 42;
    static string ReturnString() => "Hello";

    static void Main() {
        GenericDelegate<int> getInt = ReturnInt;
        GenericDelegate<string> getString = ReturnString;

        Console.WriteLine(getInt());     // 输出:42
        Console.WriteLine(getString());  // 输出:Hello
    }
}

多个泛型:

cs 复制代码
using System;

// 声明泛型委托
delegate TResult MultiGenericDelegate<T1, T2, TResult>(T1 a, T2 b);

public class Program {
    // 定义方法(必须静态)
    static string MergeData(int count, string item) {
        return $"{count}个{item}";
    }

    static void Main() {
        MultiGenericDelegate<int, string, string> combine = MergeData;
        string output = combine(3, "苹果");
        Console.WriteLine(output); // 输出:3个苹果
    }
}

四、委托的特点及用法

多播委托(Multicast Delegate)

委托支持通过 +=-= 运算符绑定多个方法,调用时会按添加顺序依次执行所有绑定的方法。这一特性使得委托可以轻松实现"一对多"的方法调用,常用于需要批量触发操作的场景。

cs 复制代码
using System;

// 声明委托
delegate void MessageDelegate(string message);

public class Program 
{
    static void LogToConsole(string msg) 
    {
        Console.WriteLine("[控制台] " + msg);
    }

    static void ShowPopup(string msg) 
    {
        Console.WriteLine("[弹窗] " + msg); // 模拟弹窗逻辑
    }

    static void Main() 
    {
        // 绑定两个方法
        MessageDelegate notify = LogToConsole;
        notify += ShowPopup;

        // 调用时依次执行
        notify("系统启动成功"); 
        // 输出:
        // [控制台] 系统启动成功
        // [弹窗] 系统启动成功
    }
}

动态切换方法逻辑

委托允许运行时动态绑定不同方法,实现灵活的策略模式。例如,根据配置切换不同的算法实现:

cs 复制代码
using System;

// 声明委托
delegate int MathOperation(int a, int b);

public class Program 
{
    static int Add(int x, int y) => x + y;
    static int Multiply(int x, int y) => x * y;

    static void Main() 
    {
        MathOperation calculator;

        // 根据用户输入切换算法
        Console.Write("选择操作 (1=加法, 2=乘法): ");
        int choice = int.Parse(Console.ReadLine());

        calculator = (choice == 1) ? (MathOperation)Add : Multiply;

        int result = calculator(3, 4);
        Console.WriteLine("结果: " + result); // 输出 7 或 12
    }
}

将方法作为参数传递

委托可以将方法作为参数传递给其他方法,实现高度灵活的代码设计。例如,定义一个通用的数据处理方法:

cs 复制代码
using System;

// 声明委托
delegate string FormatterDelegate(string input);

public class Program 
{
    // 通用格式化方法
    static string ProcessText(string text, FormatterDelegate formatter) 
    {
        return formatter(text);
    }

    // 具体格式化逻辑
    static string ToUpper(string s) => s.ToUpper();
    static string Reverse(string s) 
    {
        char[] arr = s.ToCharArray();
        Array.Reverse(arr);
        return new string(arr);
    }

    static void Main() 
    {
        string input = "hello";

        string upperText = ProcessText(input, ToUpper);
        Console.WriteLine(upperText); // 输出 "HELLO"

        string reversedText = ProcessText(input, Reverse);
        Console.WriteLine(reversedText); // 输出 "olleh"
    }
}

五、系统给我们提供好的委托

1、Action 委托

表示无返回值的方法,支持 0~16 个参数。

使用示例:

示例 1:无参数
cs 复制代码
using System;

public class Program 
{
    static void PrintHello() 
    {
        Console.WriteLine("Hello, Action!");
    }

    static void Main() 
    {
        Action action = PrintHello;
        action(); // 输出: Hello, Action!
    }
}
示例 2:带参数
cs 复制代码
using System;

public class Program 
{
    static void ShowMessage(string message, int repeat) 
    {
        for (int i = 0; i < repeat; i++) 
        {
            Console.WriteLine(message);
        }
    }

    static void Main() 
    {
        Action<string, int> actionWithParams = ShowMessage;
        actionWithParams("Action 示例", 3); 
        // 输出 3 次 "Action 示例"
    }
}

可以看出 和我们自定义的委托是一样的,只不过系统帮助我们声明好了

2、Func 委托

表示有返回值的方法,最后一个泛型参数为返回类型

示例 1:无参数有返回值
cs 复制代码
using System;

public class Program 
{
    static int GetAnswer() 
    {
        return 42;
    }

    static void Main() 
    {
        Func<int> func = GetAnswer;
        int result = func();
        Console.WriteLine(result); // 输出: 42
    }
}
示例 2:带参数有返回值
cs 复制代码
using System;

public class Program 
{
    static string Combine(int a, string b) 
    {
        return $"{a}{b}";
    }

    static void Main() 
    {
        Func<int, string, string> funcWithParams = Combine;
        string output = funcWithParams(10, "Apples");
        Console.WriteLine(output); // 输出: 10Apples
    }
}

3、Predicate<T> 委托

专用于返回布尔值的方法,常见于集合操作。

示例 1:筛选整数集合
cs 复制代码
using System;
using System.Collections.Generic;

public class Program 
{
    static bool IsEven(int number) 
    {
        return number % 2 == 0;
    }

    static void Main() 
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
        Predicate<int> predicate = IsEven;

        // 筛选所有偶数
        List<int> evenNumbers = numbers.FindAll(predicate);
        
        foreach (var num in evenNumbers) 
        {
            Console.WriteLine(num); // 输出: 2, 4
        }
    }
}
示例 2:筛选字符串集合
cs 复制代码
using System;
using System.Collections.Generic;

public class Program 
{
    static bool HasLetterA(string text) 
    {
        return text.Contains("A", StringComparison.OrdinalIgnoreCase);
    }

    static void Main() 
    {
        List<string> words = new List<string> { "Apple", "Banana", "Cherry" };
        Predicate<string> predicate = HasLetterA;

        // 筛选包含字母 A 的单词
        List<string> filteredWords = words.FindAll(predicate);
        
        foreach (var word in filteredWords) 
        {
            Console.WriteLine(word); // 输出: Apple, Banana
        }
    }
}
委托类型 返回值 典型应用场景 示例方法签名
Action void 执行无返回值的操作 void Log(string message)
Func 任意 需要返回结果的计算或处理 int Add(int a, int b)
Predicate<T> bool 集合筛选、条件判断 bool IsValid(string input)

六、总结

C#中的委托(Delegate)是一种强大的机制,允许开发者以类型安全的方式将方法视为对象进行操作。其核心概念可总结如下:

1. 本质与定义

委托本质上是一个封装方法的类(继承自System.MulticastDelegate),类似于函数指针,但更安全。它如同一个"函数容器",可存储与委托签名(参数类型、顺序、返回值)完全匹 配的方法。例如,声明delegate int Calculate(int a, int b)后,该委托可装载任何接收两个整数并返回整数的函数(如加法和减法)。
2. 核心作用

  • 动态调用方法:运行时决定调用的具体方法,如根据用户输入切换算法(加法/乘法)。

  • 实现回调机制:将方法作为参数传递,例如在数据处理时灵活注入不同的格式化逻辑(如转大写或反转字符串)。

  • 事件驱动编程 :多播委托通过+=绑定多个方法,实现事件触发时批量执行(如同时记录日志和弹出提示)。

3. 声明与使用规则

  • 使用**delegate**关键字声明,方法需严格匹配签名。

  • 实例化时可直接赋值方法名(Calculate calc = Add;)或通过new关键字。

  • 静态方法需在类中声明为**static**以供主函数调用。

4. 关键特性

  • 多播能力 :通过+=/-=绑定多个方法,调用时按添加顺序执行,常用于需要多级处理的场景(如日志系统的多输出源)。

  • 泛型支持 :如delegate TResult MultiGenericDelegate<T1, T2, TResult>(T1 a, T2 b),提升代码复用性,适应不同类型组合。

5. 系统内置委托

  • Action :无返回值方法(支持0-16参数),如Action<string>表示接收字符串无返回的操作。

  • Func :带返回值方法(最后泛型为返回类型),如Func<int, int, bool>判断两数是否相等。

  • Predicate<T> :专用于返回布尔值的方法,常见于集合筛选(如List.FindAll(predicate))。

实际应用价值

委托极大地提升了代码的灵活性与扩展性。例如,在GUI开发 中,按钮点击事件通过委托绑定多个响应逻辑;在LINQ查询 中,Where方法通过Func<T, bool>委托实现动态过滤条件。系统内置的**ActionFunc**更简化了代码,避免了重复定义委托类型的繁琐。理解委托机制,是掌握C#事件、异步编程(如async/await)及函数式编程范式的关键基础。

相关推荐
春.光明媚12 分钟前
网页端调用本地应用打开本地文件(PDF、Word、excel、PPT)
c#·注册表·网页端打开本地文件
Non importa12 分钟前
【初阶数据结构】树——二叉树(上)
c语言·数据结构·学习·算法
若水晴空初如梦17 分钟前
QT聊天项目DAY06
开发语言·qt
Rousson42 分钟前
硬件学习笔记--57 MCU相关资源介绍
笔记·单片机·mcu·学习
h汉堡2 小时前
C++入门基础
开发语言·c++·学习
HtwHUAT2 小时前
实验四 Java图形界面与事件处理
开发语言·前端·python
鄃鳕2 小时前
QSS【QT】
开发语言·qt
汤姆_5112 小时前
【c语言】深度理解指针4——sizeof和strlen
c语言·开发语言
zlbcdn2 小时前
C#处理网络传输中不完整的数据流
c#·不完整数据流
碎梦归途2 小时前
23种设计模式-结构型模式之外观模式(Java版本)
java·开发语言·jvm·设计模式·intellij-idea·外观模式