在 .NET 开发中,委托(Delegate) 是实现"方法作为参数传递"的核心机制。而 Func 与 Action 是 .NET 框架内置的预定义泛型委托,几乎覆盖了 90% 的委托使用场景------不用再手动定义自定义委托,直接用它们就能高效实现"方法传参"。
一句话核心区分(新手必记):
Func:有返回值,核心是"算出结果"
Action:无返回值,核心是"执行动作"
本文从 定义 → 示例 → 实战场景 → 易错点 → 对比总结 五个维度,把 Func/Action 讲透,看完就能直接用在项目里。
一、核心定义与语法(新手友好版)
1. Action 委托:只做事,不返回
核心特征:
- 无返回值(返回类型为
void),仅执行逻辑(比如打印、日志、修改状态) - 支持 0 ~ 16 个输入参数(满足绝大多数业务场景)
- 框架已预定义,无需手动写
delegate关键字
基础语法(常用版):
csharp
Action // 无参数,仅执行动作
Action<T> // 1个参数,无返回值
Action<T1, T2> // 2个参数,无返回值
// 最多支持 Action<T1,T2,...,T16>
2. Func 委托:算结果,必返回
核心特征:
- 必须有返回值(无参版本也得返回),核心是"计算并产出结果"
- 支持 0 ~ 16 个输入参数,最后一个泛型参数固定是返回值类型(新手最易混点)
- 同样是框架预定义,开箱即用
基础语法(常用版):
csharp
Func<TResult> // 无参数,返回 TResult 类型结果
Func<T1, TResult> // 1个参数,返回 TResult 类型结果
Func<T1, T2, TResult>// 2个参数,返回 TResult 类型结果
// 最多支持 Func<T1,T2,...,T16, TResult>
二、Action 委托实战示例(带详细注释)
示例代码(可直接复制运行)
csharp
using System;
class ActionPracticalDemo
{
static void Main()
{
// 1. 无参数 Action:执行简单动作(打印固定内容)
Action printWelcome = () => Console.WriteLine("✅ 欢迎使用 Action 委托!");
printWelcome(); // 调用:直接执行,无返回值
// 2. 1个参数 Action:接收字符串,打印姓名
Action<string> printUserName = name =>
Console.WriteLine($"👤 用户名:{name}");
printUserName("李四"); // 传入参数执行
// 3. 2个参数 Action:接收两个整数,打印求和过程(仅输出,不返回结果)
Action<int, int> printAddProcess = (num1, num2) =>
{
int sum = num1 + num2; // 仅内部使用,不返回
Console.WriteLine($"🧮 {num1} + {num2} = {sum}");
};
printAddProcess(15, 25); // 传入两个参数执行
}
}
控制台输出结果
text
✅ 欢迎使用 Action 委托!
👤 用户名:李四
🧮 15 + 25 = 40
新手提示 :
Action 的"价值"体现在"执行动作的副作用"(比如修改控制台输出、写入日志文件、更新UI),而非返回数据。
三、Func 委托实战示例(带详细注释)
示例代码(可直接复制运行)
csharp
using System;
class FuncPracticalDemo
{
static void Main()
{
// 1. 无参数 Func:返回固定字符串(核心是"产出结果")
Func<string> getSystemMessage = () => "✅ 欢迎使用 Func 委托!";
string msg = getSystemMessage(); // 调用:接收返回值
Console.WriteLine(msg);
// 2. 2个参数 Func:计算两个整数的和,返回结果
Func<int, int, int> calculateSum = (a, b) => a + b;
int sumResult = calculateSum(15, 25); // 接收返回值
Console.WriteLine($"🧮 求和结果:{sumResult}");
// 3. 2个参数 Func:计算两个小数的乘积(多行逻辑版)
Func<double, double, double> calculateProduct = (x, y) =>
{
// 可写复杂逻辑,最终必须返回值
double product = x * y;
return product;
};
double productResult = calculateProduct(4.5, 2.0);
Console.WriteLine($"🔢 乘积结果:{productResult}");
}
}
控制台输出结果
text
✅ 欢迎使用 Func 委托!
🧮 求和结果:40
🔢 乘积结果:9
新手提示 :
Func 调用后必须接收返回值(或直接使用),如果只调用不处理返回值,就失去了 Func 的核心意义。
四、实际开发中的高频场景(LINQ + 业务逻辑)
Func 和 Action 最常用在 LINQ 查询、集合操作、异步编程、回调函数 中,以下是项目中最常见的示例:
示例代码(LINQ 实战)
csharp
using System;
using System.Collections.Generic;
using System.Linq;
class LinqWithFuncAction
{
static void Main()
{
// 模拟业务数据:商品列表
List<Product> products = new()
{
new Product { Id = 1, Name = "手机", Price = 2999 },
new Product { Id = 2, Name = "耳机", Price = 199 },
new Product { Id = 3, Name = "电脑", Price = 5999 }
};
// 1. Func 场景:LINQ Where 筛选(需要返回布尔结果判断是否符合条件)
Func<Product, bool> isExpensiveProduct = p => p.Price > 2000;
var expensiveProducts = products.Where(isExpensiveProduct).ToList();
Console.WriteLine("💰 高价商品:");
foreach (var p in expensiveProducts)
{
Console.WriteLine($"- {p.Name}({p.Price}元)");
}
// 2. Action 场景:遍历集合执行动作(仅打印,无返回值)
Action<Product> printProductInfo = p =>
Console.WriteLine($"📦 商品:{p.Name},价格:{p.Price}元");
Console.WriteLine("\n📋 所有商品:");
products.ForEach(printProductInfo);
}
// 定义商品类(模拟业务实体)
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
控制台输出结果
text
💰 高价商品:
- 手机(2999元)
- 电脑(5999元)
📋 所有商品:
📦 商品:手机,价格:2999元
📦 商品:耳机,价格:199元
📦 商品:电脑,价格:5999元
核心结论:
- LINQ 中
Where/Select/FirstOrDefault等需要"返回判断/转换结果"的方法,底层都是 Func 委托; - 集合
ForEach、事件回调、日志记录等"只执行动作"的场景,优先用 Action 委托。
五、新手易错点提醒(避坑指南)
- Func 泛型参数顺序 :新手容易把返回值类型放错位置,记住「Func 的最后一个泛型参数永远是返回值」,比如
Func<int, string>是"接收 int 参数,返回 string",而非反过来; - Action 并非不能有逻辑内部的变量:Action 可以在内部定义变量、执行复杂逻辑,只是最终不返回值;
- 避免过度封装 :简单逻辑直接用 lambda 表达式(如示例中的
p => p.Price > 2000),无需单独定义委托变量,减少冗余代码。
六、Func vs Action 核心差异对比表
| 对比维度 | Func 委托 | Action 委托 |
|---|---|---|
| 返回值 | 必须有(最后一个泛型参数为返回类型) | 无(返回 void) |
| 泛型参数含义 | 前 N 个是输入参数,最后 1 个是返回值 | 所有参数都是输入参数,无返回值参数 |
| 核心用途 | 计算、筛选、转换(需要结果) | 打印、日志、遍历(仅执行动作) |
| 调用方式 | 接收返回值后使用 | 直接调用,无需接收返回值 |
| 常见场景 | LINQ 查询、业务计算、条件判断 | 集合遍历、事件处理、日志记录 |
七、总结(面试 + 实战速记)
核心要点
- 选 Func:当你需要一段逻辑"产出结果"(比如计算、筛选、转换数据)时使用;
- 选 Action:当你只需要一段逻辑"执行动作"(比如打印、写日志、更新状态)时使用;
- 新手技巧:写代码前先问自己「这段逻辑需要返回值吗?」------ 需要用 Func,不需要用 Action。
实战建议
- 日常开发优先使用框架预定义的 Func/Action,而非手动定义 delegate,减少代码量;
- LINQ 操作中,Func 是"主角",Action 仅用于遍历输出;
- 异步编程中(如 Func<Task>、Action),核心区分逻辑和同步场景一致。