Action和Func

作者:林杜雨都日期:2025/12/14

1. 为什么需要 ActionFunc

在 C# 中,我们经常需要将方法作为参数传递给其他方法,或者将方法存储在变量中以便稍后调用。传统上,我们需要先定义一个与目标方法签名完全匹配的委托类型,这非常繁琐。

例如,如果我们想传递一个没有返回值、有两个 int 参数的方法,我们需要这样写:

1// 1. 自定义委托类型
2public delegate void MyCustomDelegate(int a, int b);
3
4// 2. 定义一个符合该签名的方法
5public static void Add(int x, int y)
6{
7    Console.WriteLine($"Sum is: {x + y}");
8}
9
10// 3. 使用委托
11MyCustomDelegate del = Add;
12del(10, 20);

这个过程很啰嗦。为了解决这一问题,.NET 框架供了一系列预定义好的、通用的委托类型,ActionFunc 就是其中最核心的两个。它们让我们无需定义自定义委托,直接使用即可,极大地提高了开发效率和代码的可读性。

2. 核心概念:什么是委托?

在深入 ActionFunc 之前,必须理解委托

  • 定义:委托是一种类型安全的函数指针。它存储对一个或多个方法的引用。
  • 本质:委托是一个类,它定义了方法的签名(返回类型和参数列表)。任何与该签名匹配的方法都可以被赋值给这个委托变量。
  • 作用
    1. 将方法作为参数传递:实现高阶函数和回调机制。
    2. 事件处理:事件的底层就是基于委托实现的。
    3. 定义回调方法:例如,在异步操作完成时执行某个方法。

ActionFunc 就是 .NET 为我们预先定义好的、最常用的两种委托“模板”。

3. Action 委托

定义与作用

Action 是一个委托,它封装一个没有返回值(即返回 void)的方法

Action 是一个泛型委托,它有多个变体,可以接受 0 到 16 个不同类型的参数。

  • Action:封装一个无参数、无返回值的方法。
    public delegate void Action();
  • Action<T>:封装一个有一个参数、无返回值的方法。
    public delegate void Action<in T>(T obj);
  • Action<T1, T2>:封装一个有两个参数、无返回值的方法。
    public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
  • ...
  • Action<T1, T2, ..., T16>:封装有16个参数、无返回值的方法。

注意in 关键字表示逆变。简单理解,可以用一个比声明时更“通用”的类型的对象来调用它。例如,Action<object> 可以接受一个 string 类型的参数,因为 string 可以隐式转换为 object

原理

Action 的原理就是 .NET 框架定义好了的 public delegate void Action<...> 声明。当使用 Action<string> 时,编译器会自动将其解析为对 System.Action 泛型类型的引用。

各种使用方法与实例
实例 1:使用 Action 无参版本
1// 定义一个符合 Action 签名的方法
2static void SayHello()
3{
4    Console.WriteLine("Hello, World!");
5}
6
7// 创建 Action 委托并调用
8Action actionDelegate = SayHello;
9actionDelegate(); // 输出: Hello, World!
10
11// 也可以直接使用 Lambda 表达式
12Action anotherAction = () => Console.WriteLine("Hello from Lambda!");
13anotherAction(); // 输出: Hello from Lambda!
实例 2:使用 Action<T> 带参版本
1// 定义一个符合 Action<string> 签名的方法
2static void PrintMessage(string message)
3{
4    Console.WriteLine($"Message: {message}");
5}
6
7// 创建 Action 委托
8Action<string> printAction = PrintMessage;
9printAction("This is a test."); // 输出: Message: This is a test.
10
11// 使用 Lambda 表达式
12Action<int> printNumber = num => Console.WriteLine($"The number is: {num}");
13printNumber(42); // 输出: The number is: 42
实例 3:使用 Action<T1, T2> 多参版本
1// 定义一个符合 Action<string, int> 签名的方法
2static void LogUserActivity(string username, int activityCount)
3{
4    Console.WriteLine($"User '{username}' has performed {activityCount} activities.");
5}
6
7// 创建 Action 委托
8Action<string, int> logAction = LogUserActivity;
9logAction("Alice", 5); // 输出: User 'Alice' has performed 5 activities.
10
11// 使用 Lambda 表达式
12Action<string, bool> setStatus = (name, isActive) =>
13{
14    Console.WriteLine($"Setting status for {name} to {(isActive ? "Active" : "Inactive")}).");
15};
16setStatus("Bob", true); // 输出: Setting status for Bob to Active).
实例 4:作为方法参数

这是 Action 最强大的用途之一,允许定义一个通用的处理逻辑,而具体的操作由调用者决定。

1// 这个方法接受一个 Action<string> 作为参数
2// 它会对列表中的每个元素执行这个 Action
3static void ProcessItems(List<string> items, Action<string> processor)
4{
5    foreach (var item in items)
6    {
7        processor(item); // 调用传入的委托
8    }
9}
10
11// 调用 ProcessItems
12var names = new List<string> { "Tom", "Jerry", "Spike" };
13
14// 场景1:打印每个名字
15ProcessItems(names, name => Console.WriteLine($"Processing name: {name}"));
16
17// 场景2:将每个名字转换为大写
18ProcessItems(names, name => Console.WriteLine(name.ToUpper()));
19
20// 场景3:检查名字长度
21ProcessItems(names, name => Console.WriteLine($"'{name}' has {name.Length} letters."));

4. Func 委托

定义与作用

Func 是一个委托,它封装一个有返回值的方法

Func 也是一个泛型委托,但它与 Action 的关键区别在于它必须有一个返回类型参数Func 可以接受 0 到 16 个输入参数,但必须有且只有一个输出类型参数,并且这个输出类型参数必须放在最后。

  • Func<TResult>:封装一个无参数、有返回值的方法。
    public delegate Func<out TResult>();
  • Func<T, TResult>:封装一个有一个参数、有返回值的方法。
    public delegate Func<in T, out TResult>(T arg);
  • Func<T1, T2, TResult>:封装有两个参数、有返回值的方法。
    public delegate Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
  • ...
  • Func<T1, T2, ..., T16, TResult>:封装有16个参数、有返回值的方法。

注意out 关键字表示协变。简单理解,就是委托的返回类型可以比声明时更“具体”。例如,Func<string> 可以赋值给一个 Func<object> 类型的变量,因为 string 可以隐式转换为 object

原理

Action 类似,Func 也是 .NET 框架预定义好的委托类型,如 public delegate Func<out TResult>()。它提供了一个通用的模板来引用任何有返回值的方法。

各种使用方法与实例
实例 1:使用 Func<TResult> 无参有返回值
1// 定义一个符合 Func<int> 签名的方法
2static int GetCurrentYear()
3{
4    return DateTime.Now.Year;
5}
6
7// 创建 Func 委托
8Func<int> getYearFunc = GetCurrentYear;
9int currentYear = getYearFunc();
10Console.WriteLine($"Current year is: {currentYear}"); // 输出: Current year is: 2024
11
12// 使用 Lambda 表达式
13Func<string> getGreeting = () => "Welcome back!";
14string greeting = getGreeting();
15Console.WriteLine(greeting); // 输出: Welcome back!
实例 2:使用 Func<T, TResult> 带参有返回值
1// 定义一个符合 Func<string, int> 签名的方法
2static int GetStringLength(string text)
3{
4    return text.Length;
5}
6
7// 创建 Func 委托
8Func<string, int> lengthFunc = GetStringLength;
9int length = lengthFunc("Hello C#");
10Console.WriteLine($"Length is: {length}"); // 输出: Length is: 7
11
12// 使用 Lambda 表达式
13Func<int, bool> isEven = x => x % 2 == 0;
14Console.WriteLine($"Is 10 even? {isEven(10)}"); // 输出: Is 10 even? True
15Console.WriteLine($"Is 7 even? {isEven(7)}");  // 输出: Is 7 even? False
实例 3:使用 Func<T1, T2, TResult> 多参有返回值
1// 定义一个符合 Func<double, double, double> 签名的方法
2static double Multiply(double a, double b)
3{
4    return a * b;
5}
6
7// 创建 Func 委托
8Func<double, double, double> multiplyFunc = Multiply;
9double result = multiplyFunc(5.5, 2.0);
10Console.WriteLine($"Result: {result}"); // 输出: Result: 11
11
12// 使用 Lambda 表达式
13Func<string, string, string> concatenate = (s1, s2) => s1 + " " + s2;
14string fullString = concatenate("Hello", "World");
15Console.WriteLine(fullString); // 输出: Hello World
实例 4:作为方法参数(LINQ 的核心)

Func 在 LINQ 中无处不在,例如 WhereSelectOrderBy 等方法都大量使用 Func 作为参数。

1var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
2
3// Where 方法接受一个 Func<int, bool> 参数
4// 这个 Func 决定了元素是否应该被包含在结果中
5var evenNumbers = numbers.Where(n => n % 2 == 0);
6Console.WriteLine("Even numbers: " + string.Join(", ", evenNumbers));
7// 输出: Even numbers: 2, 4, 6, 8, 10
8
9// Select 方法接受一个 Func<int, string> 参数
10// 这个 Func 定义了如何将每个元素转换成另一种类型
11var squaredStrings = numbers.Select(n => $"Square of {n} is {n * n}");
12foreach (var s in squaredStrings)
13{
14    Console.WriteLine(s);
15}
16// 输出:
17// Square of 1 is 1
18// Square of 2 is 4
19// ...等等

5. Action vs. Func:核心区别

特性ActionFunc
返回值没有返回值 (返回 void)必须有返回值
泛型参数全部都是输入参数前 N 个是输入参数,最后一个输出(返回)类型
记忆口诀Act (行动),做了某件事,但没有结果。Function (函数),有输入,必然有输出。

如何选择?

6. 高级应用场景

Lambda 表达式

ActionFunc 与 Lambda 表达式是天作之合。Lambda 提供了一种简洁、内联的方式来创建匿名方法,直接赋值给 ActionFunc 变量。

1// Action 示例
2Action<string> log = msg => Console.WriteLine($"[LOG] {DateTime.Now}: {msg}");
3log("Application started.");
4
5// Func 示例
6Func<int, int, int> add = (x, y) => x + y;
7Func<int, int, int> multiply = (x, y) => x * y;
8
9int result1 = add(10, 20);
10int result2 = multiply(10, 20);
LINQ 中的使用

如前所述,LINQ 的查询方法高度依赖 FuncAction

1var people = new List<Person>
2{
3    new Person { Name = "Alice", Age = 30 },
4    new Person { Name = "Bob", Age = 25 },
5    new Person { Name = "Charlie", Age = 35 }
6};
7
8// Func<Person, bool> 用于筛选
9var adults = people.Where(p => p.Age >= 30);
10
11// Func<Person, string> 用于投影
12var names = people.Select(p => p.Name);
13
14// Func<Person, Person, int> 用于排序
15var sortedByName = people.OrderBy(p => p.Name);
异步编程

在异步编程中,ActionFunc 也扮演着重要角色,尤其是在回调模式中。

1// 模拟一个异步下载操作
2async Task DownloadDataAsync(string url, Action<string> onComplete)
3{
4    await Task.Delay(2000); // 模拟网络延迟
5    string data = $"Data from {url}";
6    onComplete(data); // 下载完成后,调用回调方法
7}
8
9// 调用异步方法
10Console.WriteLine("Starting download...");
11DownloadDataAsync("https://example.com", data =>
12{
13    Console.WriteLine("Download complete!");
14    Console.WriteLine($"Received: {data}");
15});
16Console.WriteLine("Download initiated, doing other work...");
事件处理

事件的 Invoke 方法在底层就是一个 Action。当订阅事件时,实际上就是在将一个方法(或 Lambda)赋值给这个 Action 委托链。

1public class Button
2{
3    // 这是一个 Action 委托
4    public event EventHandler Clicked;
5
6    public void OnClick()
7    {
8        Clicked?.Invoke(this, EventArgs.Empty);
9    }
10}
11
12var button = new Button();
13// 使用 Lambda 表达式订阅事件
14button.Clicked += (sender, e) => Console.WriteLine("Button was clicked!");
15button.OnClick(); // 输出: Button was clicked!

7. 总结

特性ActionFunc
本质预定义的、无返回值的泛型委托预定义的、有返回值的泛型委托
核心作用将“过程”或“操作”作为参数传递将“计算”或“转换”作为参数传递
语法特点Action<T1, T2, ...> (无返回类型)Func<T1, T2, ..., TResult> (最后一个参数是返回类型)
最佳实践用于封装 void 方法,如日志、UI更新、文件保存等用于封装有返回值的方法,如数据查询、数学计算、数据转换等
与 Lambda绝配,使代码简洁、匿名化绝配,是 LINQ 和现代 C# 编程的基石

Action和Func》 是转载文章,点击查看原文


相关推荐


无需修改测试用例实现Selenium四倍性能提升的完整方案
测试人社区—52722025/12/6

在测试自动化中,Selenium的执行效率直接影响项目交付速度和资源成本。本文将针对无需修改测试用例的前提,从驱动配置、执行策略及环境优化三个维度,系统介绍提升Selenium执行速度400%的实战方案。 一、浏览器驱动层深度优化 1. 启用新一代无头模式(Headless Mode) # Chrome无头模式配置示例 options = webdriver.ChromeOptions() options.add_argument('--headless=new') options.add


JWT教程
y1y1z2025/11/28

JWT技术 描述:JWT是用于根据特征值生成Token(凭证)的工具库,常用于身份校验功能 JWT特性 JWT天然携带信息,可以快速实现“多设备登录” 管理、登出、重复登录检验等功能JWT支持签名加密,开发者也可以初步校验特征值,保证了一定的安全性 token = Header + Payload + Signature Header:签名算法 + token类型(固定为JWT),例如{ "alg": "HS256","type": "JWT"}Signature:密文最后拼接密钥


Python字典元素的增、删、改操作
咖啡の猫2025/12/22

一、前言 字典(dict)是 Python 中最灵活的数据结构之一,支持动态地增加、删除、修改键值对。 然而,看似简单的操作背后,却隐藏着引用共享、内存管理、安全边界等细节。 你是否遇到过这些问题? 修改一个字典,另一个变量也跟着变了?用 d[key] = value 覆盖了重要数据却没察觉?在遍历字典时删除元素,结果报错?想批量更新配置,但代码又长又难维护? 本文将带你: ✅ 掌握字典“增、删、改”的所有核心方法 ✅ 理解 update()、字典解包、| 合并等高级技巧 ✅ 避开引用共


redis常见问题分析
哈里谢顿2026/1/1

在高并发系统中,缓存(如 Redis)与数据库(如 MySQL)配合使用是提升性能的关键手段。但若设计不当,会引发四类经典问题:双写不一致、缓存穿透、缓存雪崩、缓存击穿。下面逐一详解其原理、危害及解决方案。 一、缓存与 DB 双写不一致(Cache-DB Inconsistency) 🔍 问题描述 当数据更新时,先更新数据库,再操作缓存(删除或更新),但由于网络延迟、程序异常或并发操作,导致 缓存与数据库中的数据短暂或长期不一致。 🧩 典型场景 线程 A 更新 DB → 删除缓存 线程


计算机视觉入门到实战系列(六)边缘检测sobel算子
_codemonster2026/1/9

边缘检测 一、核心原理:变化的度量二、核心步骤(传统方法)三、经典边缘检测算子sobel算子计算X轴方向梯度计算Y轴方向梯度聚合 一、核心原理:变化的度量 边缘的本质是图像函数(灰度值、颜色值)的突然变化或不连续性。在数学上,这种“变化”可以通过导数或梯度来度量。 一维信号类比:想象一个一维的灰度信号(一条扫描线)。在平坦区域,灰度值恒定,导数为 0。在斜坡(灰度渐变)区域,导数为一个非零常数。在阶跃(灰度突变,即边缘)处,导数会达到一个极值(峰值)。扩展到二维图像:对于二


用bhyve-webadmin来管理FreeBSD系统下的bhyve虚拟机(上)
skywalk81632026/1/17

BVCP((Bhyve Virtual-Machine Control Panel ,bhyve-webadmin )是一个图形化和安全的web控制面板,旨在管理FreeBSD bhyve虚拟机。BVCP专为数据中心级可靠性而设计,专为连续24/7运行而构建,专注于稳定性和性能。它是一个本机FreeBSD应用程序,具有简单的一键安装过程,确保快速轻松的部署。BVCP独立于系统配置运行,不修改现有设置,允许它在大多数环境中平稳运行。使用BVCP,管理员可以通过单个统一的界面管理多个物理主机,而不需


OoderAgent V0.6.5 Nexus 重磅发布:开启超级智能体开发框架新纪元
OneCodeCN2026/1/26

前言: v0.6.5 使用了一个特别的代号,Nexus(枢纽)她不再是一次简单的技术升级。而是一次重生。cong 从0.6.2到0.6.5我们在AI的驱动先快速的迭代,从从基础架构到核心升级,再到技能统一提升,直到0.6.5 一次质的跃迁。本次版本以“构建个人超级终端、赋能全场景智能开发”为核心,重构技术架构、强化能力体系、拓展生态边界,为开发者提供一套从设备协同到AI能力编排的全链路智能体开发解决方案,标志着SuperAgent向“去中心化超级智能体底座”迈出关键一步。 一、Nexu

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客