C# 简述委托,Func与Action委托。 他们之前有什么区别?
C#中的委托、`Func`和`Action`。
- 1. 委托 (Delegate) - 基础概念
- 2. Func 委托
- 3. Action 委托
- 三者的区别总结
- 使用场景与建议
- 简单类比
- 1. 回调机制与异步编程
- 使用 Action 作为完成回调
- 使用 Func 进行重试机制
- 2. 策略模式与动态算法选择
- 使用 Func 实现策略模式
- 3. 事件处理与观察者模式
- 自定义委托用于事件
- 4. LINQ 扩展与复杂查询
- 使用 Func 创建动态查询
- 5. 中间件管道模式
- 使用 Func 构建处理管道
- 6. 依赖注入与工厂模式
- 使用 Func 作为工厂委托
- 关键总结
- 开发中的实际调用
- 下面是一个完整的中间件实现,使用 Func 委托构建处理管道,每行代码都有详细注解:
- 完整的中间件扩展类
- 创建不同的管道组合
- 使用模块化管道
- 关于 `await next()` 的作用和自定义操作
- `await next()` 的作用
- 具体作用:
- 执行流程示例:
- 有和没有 `await next()` 的区别:
- 管道自动触发机制
- Lambda 表达式中的自定义操作
- 可执行的自定义操作示例:
- 更具体的业务示例:
- 完整的执行流程示例
- 关于管道触发机制和自定义操作
- 管道自动触发机制
- Lambda 表达式中的自定义操作
- JSON 文件读取示例:
- 错误处理机制
- 异常触发和处理:
- 完整的错误处理流程:
- 具体的 JSON 文件操作示例
- 错误触发和处理的完整流程
- 总结
- 关键总结
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扩展
在实际复杂开发中,这些委托类型让代码更加灵活、可扩展和可测试,是实现许多设计模式的关键工具。
开发中的实际调用
下面是一个完整的中间件实现,使用 Func 委托构建处理管道,每行代码都有详细注解:
// 定义中间件上下文类,用于在中间件之间传递数据
public class MiddlewareContext
{public string RequestData { get; set; } // 请求数据public string ResponseData { get; set; } // 响应数据public DateTime StartTime { get; set; } // 开始处理时间public Dictionary<string, object> Properties { get; } = new Dictionary<string, object>(); // 自定义属性存储
}// 中间件管道构建器
public class MiddlewarePipeline
{// 存储中间件的列表。每个中间件都是一个Func委托:// - 第一个参数:MiddlewareContext 上下文对象// - 第二个参数:Func<Task> 下一个中间件的委托// - 返回值:Task 表示异步操作private readonly List<Func<MiddlewareContext, Func<Task>, Task>> _middlewares = new List<Func<MiddlewareContext, Func<Task>, Task>>();// 添加中间件到管道的方法// middleware: 要添加的中间件委托// 返回:MiddlewarePipeline 自身,支持链式调用public MiddlewarePipeline Use(Func<MiddlewareContext, Func<Task>, Task> middleware){_middlewares.Add(middleware); // 将中间件添加到列表中return this; // 返回自身,支持 builder.Use(...).Use(...) 的链式语法}// 构建并执行中间件管道// context: 中间件上下文对象,包含请求和响应数据// 返回:Task 表示异步执行完成public async Task ExecuteAsync(MiddlewareContext context){// 如果没有任何中间件,直接返回if (_middlewares.Count == 0)return;// 创建当前中间件的索引,用于跟踪执行位置int index = 0;// 定义本地函数,用于执行下一个中间件Func<Task> next = null;// 初始化next委托,这里使用递归方式构建中间件链next = async () =>{// 如果还有中间件未执行if (index < _middlewares.Count){// 获取当前要执行的中间件var currentMiddleware = _middlewares[index];index++; // 移动索引到下一个中间件// 执行当前中间件,并传入next委托(指向下一个中间件)await currentMiddleware(context, next);}};// 开始执行中间件链,从第一个中间件开始await next();}// 另一种构建方式:使用反向构建模式(更常见的中间件模式)public Task ExecuteAsyncReverse(MiddlewareContext context){// 如果没有任何中间件,直接返回完成的任务if (_middlewares.Count == 0)return Task.CompletedTask;// 从最后一个中间件开始,反向构建调用链Func<Task> pipeline = () => Task.CompletedTask; // 最内层的"下一个"委托(空操作)// 从最后一个中间件到第一个中间件,反向包装for (int i = _middlewares.Count - 1; i >= 0; i--){var currentMiddleware = _middlewares[i]; // 获取当前中间件var next = pipeline; // 保存当前的pipeline作为下一个中间件// 重新定义pipeline:执行当前中间件,并调用nextpipeline = () => currentMiddleware(context, next);}// 执行构建好的管道return pipeline();}
}
// 示例程序
class Program
{static async Task Main(string[] args){// 创建中间件管道构建器var pipelineBuilder = new MiddlewarePipeline();// 添加日志记录中间件pipelineBuilder.Use(async (context, next) =>{// 记录请求开始时间context.StartTime = DateTime.Now;Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 开始处理请求: {context.RequestData}");// 调用下一个中间件await next();// 记录处理完成时间var duration = DateTime.Now - context.StartTime;Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 处理完成,耗时: {duration.TotalMilliseconds}ms");});// 添加认证中间件pipelineBuilder.Use(async (context, next) =>{Console.WriteLine("执行认证检查...");// 模拟认证逻辑if (context.RequestData?.Contains("token=") == true){Console.WriteLine("认证成功");await next(); // 认证成功,继续执行下一个中间件}else{Console.WriteLine("认证失败");context.ResponseData = "认证失败:缺少token";// 不调用next(),中断管道执行}});// 添加数据处理中间件pipelineBuilder.Use(async (context, next) =>{Console.WriteLine("处理请求数据...");// 模拟数据处理if (context.RequestData != null){context.ResponseData = $"处理结果: {context.RequestData.ToUpper()}";context.Properties["ProcessedBy"] = "DataProcessingMiddleware";}await next(); // 继续执行(虽然这里后面没有中间件了,但保持模式一致)});// 创建上下文对象var context = new MiddlewareContext{RequestData = "hello world token=abc123"};Console.WriteLine("=== 正向构建执行 ===");// 执行管道await pipelineBuilder.ExecuteAsync(context);Console.WriteLine($"最终响应: {context.ResponseData}");Console.WriteLine();// 重置上下文,测试另一种构建方式context.ResponseData = null;Console.WriteLine("=== 反向构建执行 ===");await pipelineBuilder.ExecuteAsyncReverse(context);Console.WriteLine($"最终响应: {context.ResponseData}");}
}
详细解释
- 中间件委托签名
Func<MiddlewareContext, Func<Task>, Task>
第一个参数: MiddlewareContext - 上下文对象,用于在中间件之间传递数据
第二个参数: Func - 下一个中间件的委托(next)
返回值: Task - 异步操作任务
- 两种构建模式
-
正向构建 (ExecuteAsync):
从第一个中间件开始执行
每个中间件显式调用 await next() 来继续执行
更灵活,中间件可以决定是否继续执行
-
反向构建 (ExecuteAsyncReverse):
从最后一个中间件开始反向包装
构建完整的调用链后再执行
更符合ASP.NET Core中间件的模式
- 中间件执行流程
请求 → 中间件1 → 中间件2 → 中间件3 → 处理核心逻辑
响应 ← 中间件1 ← 中间件2 ← 中间件3 ← 处理结果
- 关键特性
-
异步支持: 所有中间件都支持异步操作
-
上下文共享: 通过 MiddlewareContext 共享数据
-
灵活控制: 中间件可以决定是否继续执行管道
-
可扩展性: 可以轻松添加新的中间件
-
错误处理: 可以通过异常处理中间件统一处理错误
- 实际应用场景
-
Web框架中间件(如ASP.NET Core)
-
消息处理管道
-
数据验证和处理流程
-
日志记录和监控
-
认证和授权检查
这种模式提供了极大的灵活性,允许你通过组合不同的中间件来构建复杂的数据处理流程。
完整的中间件扩展类
public static class MiddlewareExtensions
{// 1. 异常处理中间件public static Func<MiddlewareContext, Func<Task>, Task> ExceptionHandlingMiddleware(){return async (context, next) =>{try{await next();}catch (Exception ex){Console.WriteLine($"[异常处理] 捕获异常: {ex.Message}");context.ResponseData = $"错误: {ex.Message}";context.Properties["Exception"] = ex;}};}// 2. 性能监控中间件public static Func<MiddlewareContext, Func<Task>, Task> PerformanceMonitoringMiddleware(){return async (context, next) =>{var stopwatch = Stopwatch.StartNew();context.Properties["StartTime"] = DateTime.Now;await next();stopwatch.Stop();Console.WriteLine($"[性能监控] 执行时间: {stopwatch.ElapsedMilliseconds}ms");context.Properties["ExecutionTimeMs"] = stopwatch.ElapsedMilliseconds;};}// 3. 请求日志中间件public static Func<MiddlewareContext, Func<Task>, Task> RequestLoggingMiddleware(){return async (context, next) =>{Console.WriteLine($"[请求日志] 开始处理: {context.RequestData}");Console.WriteLine($"[请求日志] 请求时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");await next();Console.WriteLine($"[请求日志] 处理完成: {context.ResponseData}");Console.WriteLine($"[请求日志] 完成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");};}// 4. 认证授权中间件public static Func<MiddlewareContext, Func<Task>, Task> AuthenticationMiddleware(string requiredRole = null){return async (context, next) =>{// 模拟认证检查if (!context.RequestData?.Contains("auth=") == true){context.ResponseData = "认证失败: 缺少认证信息";Console.WriteLine("[认证] 认证失败");return; // 中断管道}// 模拟角色检查if (!string.IsNullOrEmpty(requiredRole) &&!context.RequestData?.Contains($"role={requiredRole}") == true){context.ResponseData = $"授权失败: 需要 {requiredRole} 角色";Console.WriteLine($"[授权] 角色 {requiredRole} 检查失败");return;}Console.WriteLine("[认证] 认证成功");context.Properties["Authenticated"] = true;context.Properties["UserRole"] = requiredRole ?? "user";await next();};}// 5. 数据验证中间件public static Func<MiddlewareContext, Func<Task>, Task> ValidationMiddleware(Func<string, bool> validationRule){return async (context, next) =>{if (string.IsNullOrEmpty(context.RequestData)){context.ResponseData = "验证失败: 请求数据为空";Console.WriteLine("[验证] 数据为空");return;}if (!validationRule(context.RequestData)){context.ResponseData = "验证失败: 数据格式不正确";Console.WriteLine("[验证] 数据格式验证失败");return;}Console.WriteLine("[验证] 数据验证成功");await next();};}// 6. 缓存中间件public static Func<MiddlewareContext, Func<Task>, Task> CachingMiddleware(IDictionary<string, string> cache, int cacheDurationMs = 5000){return async (context, next) =>{var cacheKey = context.RequestData;// 检查缓存if (cache.TryGetValue(cacheKey, out var cachedResponse)){context.ResponseData = $"[缓存命中] {cachedResponse}";Console.WriteLine($"[缓存] 缓存命中: {cacheKey}");return;}await next();// 缓存结果if (!string.IsNullOrEmpty(context.ResponseData)){cache[cacheKey] = context.ResponseData;Console.WriteLine($"[缓存] 已缓存结果: {cacheKey}");}};}// 7. 限流中间件// 使用包含计数器的共享对象public class RequestCounter{public RequestCounter(int count , DateTime lastReset){this.Count = count;this.LastReset = lastReset;}public int Count { get; set; }public DateTime LastReset { get; set; } = DateTime.Now;}public static Func<MiddlewareContext, Func<Task>, Task> RateLimitingMiddleware(RequestCounter counter, int maxRequestsPerMinute = 10){return async (context, next) =>{// 每分钟重置if ((DateTime.Now - counter.LastReset).TotalMinutes >= 1){counter.Count = 0;counter.LastReset = DateTime.Now;}if (counter.Count >= maxRequestsPerMinute){context.ResponseData = "限流: 请求过于频繁";Console.WriteLine("[限流] 请求被限制");return;}counter.Count++;Console.WriteLine($"[限流] 当前请求计数: {counter.Count}/{maxRequestsPerMinute}");await next();};}// 8. 数据转换中间件public static Func<MiddlewareContext, Func<Task>, Task> DataTransformationMiddleware(Func<string, string> transformFunction){return async (context, next) =>{if (!string.IsNullOrEmpty(context.RequestData)){context.RequestData = transformFunction(context.RequestData);Console.WriteLine($"[数据转换] 转换后数据: {context.RequestData}");}await next();if (!string.IsNullOrEmpty(context.ResponseData)){context.ResponseData = transformFunction(context.ResponseData);Console.WriteLine($"[数据转换] 转换后响应: {context.ResponseData}");}};}// 9. 重试中间件public static Func<MiddlewareContext, Func<Task>, Task> RetryMiddleware(int maxRetries = 3){return async (context, next) =>{int attempt = 0;while (attempt < maxRetries){try{attempt++;Console.WriteLine($"[重试] 第 {attempt} 次尝试");await next();return; // 成功则退出}catch (Exception ex) when (attempt < maxRetries){Console.WriteLine($"[重试] 第 {attempt} 次尝试失败: {ex.Message}");await Task.Delay(100 * attempt); // 指数退避}}};}// 10. 压缩中间件public static Func<MiddlewareContext, Func<Task>, Task> CompressionMiddleware(){return async (context, next) =>{// 模拟压缩前的大小var originalSize = context.RequestData?.Length ?? 0;Console.WriteLine($"[压缩] 原始数据大小: {originalSize} bytes");await next();// 模拟压缩后的响应if (!string.IsNullOrEmpty(context.ResponseData)){var compressedSize = context.ResponseData.Length / 2; // 模拟压缩context.Properties["CompressionRatio"] = (double)compressedSize / originalSize;Console.WriteLine($"[压缩] 压缩后大小: {compressedSize} bytes, 压缩比: {compressedSize / (double)originalSize:P}");}};}
}
创建不同的管道组合
public static class PipelineFactory
{// API处理管道public static MiddlewarePipeline CreateApiPipeline(){var cache = new Dictionary<string, string>();int requestCount = 0;var requestCounter = new RequestCounter(requestCount , DateTime.Now);return new MiddlewarePipeline().Use(MiddlewareExtensions.RequestLoggingMiddleware()).Use(MiddlewareExtensions.PerformanceMonitoringMiddleware()).Use(MiddlewareExtensions.ExceptionHandlingMiddleware()).Use(MiddlewareExtensions.AuthenticationMiddleware("api_user")).Use(MiddlewareExtensions.RateLimitingMiddleware(requestCounter, 100)).Use(MiddlewareExtensions.CachingMiddleware(cache));}// 数据处理管道public static MiddlewarePipeline CreateDataProcessingPipeline(){return new MiddlewarePipeline().Use(MiddlewareExtensions.ValidationMiddleware(data => data.Length > 0)).Use(MiddlewareExtensions.DataTransformationMiddleware(data => data.Trim())).Use(MiddlewareExtensions.RetryMiddleware(3)).Use(MiddlewareExtensions.CompressionMiddleware());}// 监控管道public static MiddlewarePipeline CreateMonitoringPipeline(){return new MiddlewarePipeline().Use(MiddlewareExtensions.RequestLoggingMiddleware()).Use(MiddlewareExtensions.PerformanceMonitoringMiddleware()).Use(MiddlewareExtensions.ExceptionHandlingMiddleware());}
}
使用模块化管道
class ModularExample
{static async Task RunExamples(){// 使用API管道var apiPipeline = PipelineFactory.CreateApiPipeline();apiPipeline.Use(async (context, next) =>{context.ResponseData = $"API响应: {context.RequestData}";await next();});// 使用数据处理管道var dataPipeline = PipelineFactory.CreateDataProcessingPipeline();dataPipeline.Use(async (context, next) =>{context.ResponseData = $"处理后的数据: {context.RequestData}";await next();});// 组合使用Console.WriteLine("=== 组合管道使用 ===");var combinedContext = new MiddlewareContext { RequestData = " combined_pipeline_test " };// 先通过数据处理管道await dataPipeline.ExecuteAsync(combinedContext);Console.WriteLine($"数据处理结果: {combinedContext.ResponseData}");// 再通过API管道(使用处理后的数据作为新请求)var apiContext = new MiddlewareContext { RequestData = combinedContext.ResponseData };await apiPipeline.ExecuteAsync(apiContext);Console.WriteLine($"API处理结果: {apiContext.ResponseData}");}
}
关于 await next()
的作用和自定义操作
await next()
的作用
await next()
是中间件管道中的核心机制,它表示:“执行管道中的下一个中间件”。
具体作用:
- 控制流转:将执行权传递给下一个中间件
- 形成调用链:构建完整的中间件处理流水线
- 支持双向处理:允许在下一个中间件执行前后添加逻辑
执行流程示例:
// 管道构建顺序
pipeline.Use(Middleware1) // 第一个.Use(Middleware2) // 第二个 .Use(Middleware3) // 第三个.Use(FinalHandler); // 最后一个// 执行时的调用顺序:
// Middleware1 → Middleware2 → Middleware3 → FinalHandler
// 然后反向返回:FinalHandler → Middleware3 → Middleware2 → Middleware1
有和没有 await next()
的区别:
有 await next()
:
async (context, next) =>
{Console.WriteLine("前处理"); // ← 先执行await next(); // ← 调用下一个中间件Console.WriteLine("后处理"); // ← 等下一个中间件完成后执行
}
没有 await next()
:
async (context, next) =>
{Console.WriteLine("只执行这个"); // ← 执行后就结束,不会调用后续中间件// 没有 await next(),管道在此中断
}
管道自动触发机制
在您的代码中:
// API处理管道
public static MiddlewarePipeline CreateApiPipeline()
{var cache = new Dictionary<string, string>();int requestCount = 0;return new MiddlewarePipeline().Use(MiddlewareExtensions.RequestLoggingMiddleware()) // 1.Use(MiddlewareExtensions.PerformanceMonitoringMiddleware()) // 2.Use(MiddlewareExtensions.ExceptionHandlingMiddleware()) // 3.Use(MiddlewareExtensions.AuthenticationMiddleware("api_user")) // 4.Use(MiddlewareExtensions.RateLimitingMiddleware(ref requestCount, 100)) // 5.Use(MiddlewareExtensions.CachingMiddleware(cache)); // 6
}// 添加自定义中间件
apiPipeline.Use(async (context, next) =>
{context.ResponseData = $"API响应: {context.RequestData}";await next(); // ← 这里会触发什么?
});
当调用 await next()
时:
- 如果这是第7个中间件,它会调用第8个中间件(如果有)
- 如果没有更多中间件,
next()
就是一个空操作 - 执行流程会继续返回到调用链
Lambda 表达式中的自定义操作
是的,完全可以自定义各种操作! Lambda 表达式内部就是一个完整的方法体。
可执行的自定义操作示例:
apiPipeline.Use(async (context, next) =>
{// 1. 数据处理和转换var originalData = context.RequestData;context.RequestData = PreprocessData(originalData);// 2. 业务逻辑验证if (!IsValidRequest(context)){context.ResponseData = "请求无效";return; // 不调用 next(),直接中断}// 3. 记录日志和监控LogRequestDetails(context);var stopwatch = Stopwatch.StartNew();// 4. 调用下一个中间件(核心)await next();// 5. 后处理(在下一个中间件完成后执行)stopwatch.Stop();LogResponseDetails(context, stopwatch.ElapsedMilliseconds);// 6. 响应数据加工context.ResponseData = FormatResponse(context.ResponseData);// 7. 设置响应头等信息context.Properties["ProcessedBy"] = "CustomMiddleware";context.Properties["ProcessingTime"] = stopwatch.ElapsedMilliseconds;
});
更具体的业务示例:
// 自定义身份验证中间件
apiPipeline.Use(async (context, next) =>
{// 解析请求数据var queryParams = ParseQueryString(context.RequestData);// 检查tokenif (!queryParams.ContainsKey("token") || !ValidateToken(queryParams["token"])){context.ResponseData = "{\"error\": \"认证失败\", \"code\": 401}";context.Properties["StatusCode"] = 401;return; // 认证失败,中断管道}// 设置用户信息var userInfo = GetUserInfoFromToken(queryParams["token"]);context.Properties["UserId"] = userInfo.Id;context.Properties["UserRole"] = userInfo.Role;Console.WriteLine($"用户 {userInfo.Name} 请求: {context.RequestData}");await next(); // 继续执行后续中间件// 后处理:添加响应头context.ResponseData = AddResponseHeaders(context.ResponseData, userInfo);
});// 数据格式转换中间件
apiPipeline.Use(async (context, next) =>
{// 如果是JSON请求,解析为对象if (context.RequestData?.StartsWith("{") == true){try{var jsonObject = JsonSerializer.Deserialize<JsonElement>(context.RequestData);context.Properties["ParsedJson"] = jsonObject;// 可以在这里进行数据验证或转换if (!jsonObject.TryGetProperty("requiredField", out _)){context.ResponseData = "{\"error\": \"缺少必要字段\"}";return;}}catch (JsonException){context.ResponseData = "{\"error\": \"JSON格式错误\"}";return;}}await next();// 确保响应是JSON格式if (string.IsNullOrEmpty(context.ResponseData) || !context.ResponseData.Trim().StartsWith("{")){context.ResponseData = $"{{\"result\": \"{context.ResponseData}\"}}";}
});// 性能监控和统计中间件
apiPipeline.Use(async (context, next) =>
{var startTime = DateTime.UtcNow;var memoryBefore = GC.GetTotalMemory(false);try{await next(); // 执行业务逻辑var duration = (DateTime.UtcNow - startTime).TotalMilliseconds;var memoryUsed = GC.GetTotalMemory(false) - memoryBefore;// 记录性能指标Console.WriteLine($"请求处理完成 - 耗时: {duration}ms, 内存使用: {memoryUsed}bytes");context.Properties["ProcessingTimeMs"] = duration;context.Properties["MemoryUsageBytes"] = memoryUsed;}finally{// 确保资源清理,即使出现异常CleanupTemporaryResources();}
});
完整的执行流程示例
// 构建管道
var pipeline = new MiddlewarePipeline().Use(async (ctx, next) => {Console.WriteLine("中间件1: 开始");await next();Console.WriteLine("中间件1: 结束");}).Use(async (ctx, next) => {Console.WriteLine("中间件2: 开始");ctx.ResponseData = "处理中...";await next();Console.WriteLine("中间件2: 结束");}).Use(async (ctx, next) => {Console.WriteLine("中间件3: 处理业务逻辑");ctx.ResponseData = "最终结果";// 这里没有 await next(),因为是最后一个});// 执行输出:
// 中间件1: 开始
// 中间件2: 开始
// 中间件3: 处理业务逻辑
// 中间件2: 结束
// 中间件1: 结束
关于管道触发机制和自定义操作
管道自动触发机制
在您的代码中:
// API处理管道
public static MiddlewarePipeline CreateApiPipeline()
{var cache = new Dictionary<string, string>();int requestCount = 0;return new MiddlewarePipeline().Use(MiddlewareExtensions.RequestLoggingMiddleware()) // 1.Use(MiddlewareExtensions.PerformanceMonitoringMiddleware()) // 2.Use(MiddlewareExtensions.ExceptionHandlingMiddleware()) // 3.Use(MiddlewareExtensions.AuthenticationMiddleware("api_user")) // 4.Use(MiddlewareExtensions.RateLimitingMiddleware(ref requestCount, 100)) // 5.Use(MiddlewareExtensions.CachingMiddleware(cache)); // 6
}// 添加自定义中间件
apiPipeline.Use(async (context, next) =>
{context.ResponseData = $"API响应: {context.RequestData}";await next(); // ← 这里会触发什么?
});
当调用 ExecuteAsync()
时:
- 管道会按顺序执行所有中间件:1 → 2 → 3 → 4 → 5 → 6 → 您的自定义中间件
- 每个中间件中的
await next()
会调用下一个中间件 - 最后一个中间件通常不调用
next()
,或者next()
是空操作
Lambda 表达式中的自定义操作
是的,完全可以自定义各种业务操作! 包括 JSON 文件读取、数据库操作、业务逻辑处理等。
JSON 文件读取示例:
apiPipeline.Use(async (context, next) =>
{try{// 1. 自定义业务逻辑:读取JSON配置文件var configPath = "appsettings.json";if (File.Exists(configPath)){var jsonContent = await File.ReadAllTextAsync(configPath);var config = JsonSerializer.Deserialize<AppConfig>(jsonContent);context.Properties["AppConfig"] = config;Console.WriteLine($"已加载配置文件: {config?.Environment}");}// 2. 根据请求参数处理业务if (context.RequestData?.Contains("get_user") == true){var userId = ExtractUserId(context.RequestData);var userData = await LoadUserDataFromJson(userId);context.Properties["UserData"] = userData;}// 3. 调用下一个中间件await next();// 4. 后处理:将响应转换为JSON格式if (!string.IsNullOrEmpty(context.ResponseData)){var responseObj = new { success = true, data = context.ResponseData,timestamp = DateTime.UtcNow };context.ResponseData = JsonSerializer.Serialize(responseObj);}}catch (Exception ex){// 自定义错误处理context.ResponseData = $"{{\"error\": \"业务处理失败: {ex.Message}\"}}";context.Properties["HasError"] = true;// 不调用 await next(),直接返回错误}
});
错误处理机制
异常触发和处理:
apiPipeline.Use(async (context, next) =>
{try{// 自定义业务操作 - 可能抛出异常var jsonData = await ReadJsonFile("data.json");var processedData = ProcessBusinessLogic(jsonData);// 存储处理结果到上下文context.Properties["ProcessedData"] = processedData;// 继续执行下一个中间件await next();}catch (FileNotFoundException ex){// 文件不存在的特定处理context.ResponseData = $"{{\"error\": \"文件不存在: {ex.FileName}\", \"code\": \"FILE_NOT_FOUND\"}}";context.Properties["StatusCode"] = 404;Console.WriteLine($"文件读取失败: {ex.Message}");// 不调用 next(),中断管道执行}catch (JsonException ex){// JSON解析错误的特定处理context.ResponseData = $"{{\"error\": \"JSON格式错误: {ex.Message}\", \"code\": \"INVALID_JSON\"}}";context.Properties["StatusCode"] = 400;Console.WriteLine($"JSON解析错误: {ex.Message}");// 不调用 next(),中断管道执行}catch (Exception ex){// 其他未知错误的处理context.ResponseData = $"{{\"error\": \"系统内部错误\", \"code\": \"INTERNAL_ERROR\"}}";context.Properties["StatusCode"] = 500;Console.WriteLine($"未知错误: {ex.Message}");// 可以选择重新抛出,让异常处理中间件捕获throw;}
});
完整的错误处理流程:
// 构建包含异常处理的管道
var pipeline = new MiddlewarePipeline().Use(MiddlewareExtensions.ExceptionHandlingMiddleware()) // 全局异常捕获.Use(MiddlewareExtensions.RequestLoggingMiddleware()).Use(async (context, next) =>{// 自定义业务中间件 - 读取JSON文件try{Console.WriteLine("开始处理JSON文件...");// 模拟可能失败的操作if (new Random().Next(0, 4) == 0) // 25% 失败率throw new FileNotFoundException("data.json not found");var jsonContent = await File.ReadAllTextAsync("data.json");var data = JsonSerializer.Deserialize<dynamic>(jsonContent);context.Properties["JsonData"] = data;Console.WriteLine("JSON文件读取成功");await next();}catch (Exception ex) when (ex is FileNotFoundException or JsonException){// 处理特定异常,不继续传递context.ResponseData = $"{{\"error\": \"数据处理失败: {ex.Message}\"}}";Console.WriteLine($"业务处理失败: {ex.Message}");// 不调用 next(),直接返回错误响应}}).Use(async (context, next) =>{// 只有上一个中间件成功时才会执行到这里var jsonData = context.Properties["JsonData"] as dynamic;context.ResponseData = ProcessBusinessData(jsonData);await next();});// 执行测试
var context = new MiddlewareContext { RequestData = "process_data" };
await pipeline.ExecuteAsync(context);
Console.WriteLine($"最终响应: {context.ResponseData}");
具体的 JSON 文件操作示例
public static class JsonBusinessMiddleware
{// 读取JSON配置的中间件public static Func<MiddlewareContext, Func<Task>, Task> JsonConfigMiddleware(string configPath){return async (context, next) =>{try{if (!File.Exists(configPath)){context.ResponseData = $"{{\"error\": \"配置文件不存在: {configPath}\"}}";return;}var jsonContent = await File.ReadAllTextAsync(configPath);var config = JsonSerializer.Deserialize<Dictionary<string, string>>(jsonContent);// 将配置存储到上下文,供后续中间件使用foreach (var item in config){context.Properties[$"Config:{item.Key}"] = item.Value;}Console.WriteLine($"已加载 {config.Count} 个配置项");await next();}catch (Exception ex){context.ResponseData = $"{{\"error\": \"配置加载失败: {ex.Message}\"}}";// 可以选择记录日志但不中断,或者重新抛出}};}// 数据处理中间件public static Func<MiddlewareContext, Func<Task>, Task> JsonDataProcessingMiddleware(){return async (context, next) =>{// 检查是否有需要处理的数据if (context.RequestData?.StartsWith("process:") == true){var dataId = context.RequestData.Substring("process:".Length);var dataFilePath = $"data/{dataId}.json";try{if (File.Exists(dataFilePath)){var dataContent = await File.ReadAllTextAsync(dataFilePath);var data = JsonSerializer.Deserialize<BusinessData>(dataContent);// 执行业务逻辑处理var result = ProcessBusinessData(data);context.ResponseData = JsonSerializer.Serialize(result);Console.WriteLine($"数据处理完成: {dataId}");}else{context.ResponseData = $"{{\"error\": \"数据文件不存在: {dataId}\"}}";}}catch (Exception ex){context.ResponseData = $"{{\"error\": \"数据处理错误: {ex.Message}\"}}";context.Properties["ProcessingError"] = ex;}}await next();};}
}// 使用示例
var pipeline = new MiddlewarePipeline().Use(JsonBusinessMiddleware.JsonConfigMiddleware("appsettings.json")).Use(JsonBusinessMiddleware.JsonDataProcessingMiddleware()).Use(async (context, next) =>{// 最终格式化响应if (context.Properties.ContainsKey("ProcessingError")){context.Properties["StatusCode"] = 500;}else if (context.ResponseData?.Contains("error") == true){context.Properties["StatusCode"] = 400;}await next();});
错误触发和处理的完整流程
- 异常抛出:在自定义操作中(如文件读取、JSON解析)可能抛出异常
- 本地捕获:可以在当前中间件的 try-catch 中处理特定异常
- 全局处理:未被处理的异常会被管道中的异常处理中间件捕获
- 流程控制:通过调用或不调用
await next()
来控制管道继续或中断
总结
- 可以完全自定义业务逻辑,包括文件IO、JSON处理、数据库操作等
- 错误会自动触发,需要在中间件中进行适当的异常处理
- 可以通过 try-catch 局部处理或依赖全局异常处理中间件
- 调用
await next()
决定是否继续执行后续中间件 - 上下文对象(MiddlewareContext) 用于在中间件间传递数据和状态
这种设计让您能够灵活地处理各种业务场景,同时保持良好的错误处理机制。
关键总结
await next()
是中间件管道的核心,控制执行流程- Lambda 内部可以完全自定义任何业务逻辑
- 可以在
await next()
前后添加预处理和后处理逻辑 - 可以选择不调用
await next()
来中断管道执行 - 中间件顺序很重要,执行流程按添加顺序进行
这种模式提供了极大的灵活性,让您可以像组装乐高积木一样构建复杂的处理流程。