C# 简述委托,Func与Action委托。 他们之前有什么区别?
C#中的委托、Func
和Action
。
1. 委托 (Delegate) - 基础概念
是什么?
委托是一个类型安全的函数指针,它定义了方法的签名(返回值类型和参数列表)。它允许你将方法作为参数传递,或者将方法存储在变量中,用于实现回调方法和事件处理。
核心思想: 后期绑定。让你能够在运行时决定要调用哪个方法。
如何声明?
使用 delegate
关键字。
// 1. 声明一个委托类型,它指向任何“接受一个string参数并返回void”的方法
public delegate void MyDelegate(string message);// 2. 定义一个符合签名的方法
public void MyMethod(string msg)
{Console.WriteLine("Message: " + msg);
}// 3. 使用
MyDelegate del = new MyDelegate(MyMethod); // 创建委托实例
del("Hello Delegate"); // 通过委托调用方法
2. Func 委托
是什么?
Func
是.NET框架提供的泛型内置委托。它代表一个有返回值的方法。
签名:
Func
有一系列重载,最多可以接受16个输入参数。
Func<TResult>
- 没有参数,返回TResult
类型。Func<T1, TResult>
- 接受一个T1
类型的参数,返回TResult
类型。Func<T1, T2, TResult>
- 接受两个参数,返回TResult
类型。- …
Func<T1, T2, ..., T16, TResult>
- 接受16个参数,返回TResult
类型。
最后一个泛型参数始终是返回值类型!
// 示例:一个接受两个int参数并返回一个int的方法
Func<int, int, int> add = (a, b) => a + b;
int result = add(5, 3); // result = 8// 示例:一个接受string参数并返回bool的方法
Func<string, bool> isLong = s => s.Length > 5;
bool longEnough = isLong("Hello World"); // longEnough = true
3. Action 委托
是什么?
Action
也是.NET框架提供的泛型内置委托。它代表一个没有返回值(void
)的方法。
签名:
和 Func
一样,Action
也有一系列重载,最多可以接受16个输入参数,但它没有返回值。
Action
- 没有参数,没有返回值。Action<T1>
- 接受一个T1
类型的参数,没有返回值。Action<T1, T2>
- 接受两个参数,没有返回值。- …
Action<T1, T2, ..., T16>
- 接受16个参数,没有返回值。
// 示例:一个接受一个string参数但没有返回值的方法
Action<string> log = message => Console.WriteLine($"Log: {message}");
log("Something happened!"); // 输出:Log: Something happened!// 示例:一个没有参数也没有返回值的方法
Action greet = () => Console.WriteLine("Hello!");
greet();
三者的区别总结
特性 | 自定义 Delegate | Func 委托 | Action 委托 |
---|---|---|---|
返回值 | 必须明确声明 | 必须有返回值 | 必须没有返回值 (void) |
声明方式 | 需要使用 delegate 关键字自定义 | .NET 内置,无需自定义 | .NET 内置,无需自定义 |
便利性 | 麻烦,需要先定义类型才能使用 | 非常方便,直接使用 | 非常方便,直接使用 |
用途 | 早期C#版本,或需要更清晰语义时(如事件) | 代表有返回值的操作 | 代表执行一个操作(无返回值) |
参数 | 自定义 | 最后一个是返回类型,前面是输入参数 | 所有泛型参数都是输入参数 |
使用场景与建议
-
自定义委托:
- 现在主要用于定义事件(
event
关键字),因为事件需要明确的委托类型。 - 当委托签名需要非常清晰的语义,并且会在代码中大量重复使用时(例如
EventHandler
)。
- 现在主要用于定义事件(
-
Func 委托:
- LINQ 查询中大量使用(例如
.Where(x => x > 5)
,这里的 lambda 表达式就是一个Func<T, bool>
)。 - 任何需要传递或返回一个计算逻辑的场景。
- LINQ 查询中大量使用(例如
-
Action 委托:
- 用于执行一个操作。例如,启动一个新线程
Task.Run(() => { /* 做某些事 */ })
。 - 执行回调方法,通知某件事已完成,但不需要返回结果。
- 用于执行一个操作。例如,启动一个新线程
简单类比
- 自定义委托:像是自己动手打造一个专门的工具。
- Func/Action:像是从工具箱里直接拿标准化的通用工具,省时省力。
现代C#开发中,除非必要(如事件),应优先使用 Func
和 Action
,它们极大地减少了代码量,提高了开发效率。
通过一些实际开发中的复杂场景来深入理解委托、Func
和Action
的用法。
1. 回调机制与异步编程
使用 Action 作为完成回调
public class DataProcessor
{// 使用Action作为处理完成后的回调public void ProcessDataAsync(string data, Action<string> onCompleted, Action<Exception> onError = null){Task.Run(() =>{try{// 模拟耗时处理Thread.Sleep(1000);var result = data.ToUpper() + "_PROCESSED";// 在主线程执行回调(假设有同步上下文)onCompleted?.Invoke(result);}catch (Exception ex){onError?.Invoke(ex);}});}
}// 使用示例
var processor = new DataProcessor();
processor.ProcessDataAsync("hello world", result => Console.WriteLine($"处理结果: {result}"),error => Console.WriteLine($"错误: {error.Message}"));
使用 Func 进行重试机制
public static class RetryHelper
{public static TResult ExecuteWithRetry<TResult>(Func<TResult> operation, int maxRetries = 3, int delayMs = 1000){int attempts = 0;while (true){try{attempts++;return operation();}catch (Exception ex) when (attempts < maxRetries){Console.WriteLine($"尝试 {attempts} 失败,{delayMs}ms后重试。错误: {ex.Message}");Thread.Sleep(delayMs);}}}
}// 使用示例
var result = RetryHelper.ExecuteWithRetry(() =>
{// 模拟不可靠的操作(如网络请求、数据库操作)if (new Random().Next(0, 5) == 0) // 20% 失败率throw new Exception("操作失败");return DateTime.Now.ToString();
});Console.WriteLine($"最终结果: {result}");
2. 策略模式与动态算法选择
使用 Func 实现策略模式
public class PriceCalculator
{private readonly Func<decimal, decimal> _discountStrategy;// 通过构造函数注入策略public PriceCalculator(Func<decimal, decimal> discountStrategy){_discountStrategy = discountStrategy;}public decimal CalculateFinalPrice(decimal basePrice){return _discountStrategy(basePrice);}
}// 定义不同的策略
public static class DiscountStrategies
{public static decimal NoDiscount(decimal price) => price;public static decimal TenPercentDiscount(decimal price) => price * 0.9m;public static decimal SeasonalDiscount(decimal price) => price > 100 ? price - 20 : price;
}// 使用示例
var calculator = new PriceCalculator(DiscountStrategies.SeasonalDiscount);
var finalPrice = calculator.CalculateFinalPrice(150m);
Console.WriteLine($"最终价格: {finalPrice}");// 动态切换策略
calculator = new PriceCalculator(DiscountStrategies.TenPercentDiscount);
finalPrice = calculator.CalculateFinalPrice(150m);
Console.WriteLine($"折扣后价格: {finalPrice}");
3. 事件处理与观察者模式
自定义委托用于事件
public class OrderService
{// 使用自定义委托定义事件(更清晰语义)public delegate void OrderProcessedEventHandler(Order order, DateTime processedTime);public event OrderProcessedEventHandler OrderProcessed;// 使用Action定义简单事件public event Action<Order> OrderCreated;public void ProcessOrder(Order order){// 处理订单逻辑...Console.WriteLine($"处理订单: {order.Id}");// 触发事件OrderProcessed?.Invoke(order, DateTime.Now);OrderCreated?.Invoke(order);}
}// 使用示例
var orderService = new OrderService();// 订阅事件
orderService.OrderProcessed += (order, time) => Console.WriteLine($"订单 {order.Id} 在 {time} 处理完成");orderService.OrderCreated += order =>Console.WriteLine($"新订单创建: {order.Id}");orderService.ProcessOrder(new Order { Id = "ORD001", Amount = 99.99m });
4. LINQ 扩展与复杂查询
使用 Func 创建动态查询
public static class QueryExtensions
{public static IEnumerable<T> WhereDynamic<T>(this IEnumerable<T> source, Func<T, bool> predicate = null,Func<T, bool> additionalCondition = null){var query = source;if (predicate != null)query = query.Where(predicate);if (additionalCondition != null)query = query.Where(additionalCondition);return query;}public static IEnumerable<TResult> SelectDynamic<T, TResult>(this IEnumerable<T> source,Func<T, TResult> selector){return source.Select(selector);}
}// 使用示例
var users = new List<User>
{new User { Name = "Alice", Age = 25, IsActive = true },new User { Name = "Bob", Age = 30, IsActive = false },new User { Name = "Charlie", Age = 35, IsActive = true }
};// 动态构建查询
var activeUsers = users.WhereDynamic(u => u.IsActive);
var activeUserNames = users.WhereDynamic(u => u.IsActive).SelectDynamic(u => u.Name);// 更复杂的条件组合
Func<User, bool> ageFilter = u => u.Age > 28;
Func<User, bool> nameFilter = u => u.Name.StartsWith("C");var filteredUsers = users.WhereDynamic(ageFilter, nameFilter);
5. 中间件管道模式
使用 Func 构建处理管道
public class ProcessingPipeline<T>
{private readonly List<Func<T, Func<T>, T>> _middlewares = new List<Func<T, Func<T>, T>>();public ProcessingPipeline<T> Use(Func<T, Func<T>, T> middleware){_middlewares.Add(middleware);return this;}public T Execute(T initialContext){Func<T> next = () => initialContext;// 反向构建中间件链for (int i = _middlewares.Count - 1; i >= 0; i--){var currentMiddleware = _middlewares[i];var nextCopy = next;next = () => currentMiddleware(initialContext, nextCopy);}return next();}
}// 使用示例
var pipeline = new ProcessingPipeline<string>().Use((context, next) => {Console.WriteLine($"Middleware 1 前: {context}");var result = next();Console.WriteLine($"Middleware 1 后: {result}");return result + "_processed1";}).Use((context, next) => {Console.WriteLine($"Middleware 2 前: {context}");var result = next();Console.WriteLine($"Middleware 2 后: {result}");return result.ToUpper();});var finalResult = pipeline.Execute("initial_data");
Console.WriteLine($"最终结果: {finalResult}");
6. 依赖注入与工厂模式
使用 Func 作为工厂委托
public interface ILogger
{void Log(string message);
}public class FileLogger : ILogger
{private readonly string _filePath;public FileLogger(string filePath) => _filePath = filePath;public void Log(string message) => File.AppendAllText(_filePath, message + Environment.NewLine);
}public class ConsoleLogger : ILogger
{public void Log(string message) => Console.WriteLine(message);
}public class LoggerFactory
{private readonly Func<string, ILogger> _fileLoggerFactory;private readonly Func<ILogger> _consoleLoggerFactory;public LoggerFactory(Func<string, ILogger> fileLoggerFactory, Func<ILogger> consoleLoggerFactory){_fileLoggerFactory = fileLoggerFactory;_consoleLoggerFactory = consoleLoggerFactory;}public ILogger CreateFileLogger(string path) => _fileLoggerFactory(path);public ILogger CreateConsoleLogger() => _consoleLoggerFactory();
}// 在依赖注入容器中注册(伪代码)
// services.AddSingleton<Func<string, ILogger>>(path => new FileLogger(path));
// services.AddSingleton<Func<ILogger>>(() => new ConsoleLogger());
// services.AddSingleton<LoggerFactory>();
关键总结
- 自定义委托:主要用于事件和需要明确语义的场合
- Action:用于执行操作,无返回值,适合回调、事件简化版
- Func:用于计算和转换,有返回值,适合策略模式、工厂方法、LINQ扩展
在实际复杂开发中,这些委托类型让代码更加灵活、可扩展和可测试,是实现许多设计模式的关键工具。