当前位置: 首页 > java >正文

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();

三者的区别总结

特性自定义 DelegateFunc 委托Action 委托
返回值必须明确声明必须有返回值必须没有返回值 (void)
声明方式需要使用 delegate 关键字自定义.NET 内置,无需自定义.NET 内置,无需自定义
便利性麻烦,需要先定义类型才能使用非常方便,直接使用非常方便,直接使用
用途早期C#版本,或需要更清晰语义时(如事件)代表有返回值的操作代表执行一个操作(无返回值)
参数自定义最后一个是返回类型,前面是输入参数所有泛型参数都是输入参数

使用场景与建议

  1. 自定义委托

    • 现在主要用于定义事件event关键字),因为事件需要明确的委托类型。
    • 当委托签名需要非常清晰的语义,并且会在代码中大量重复使用时(例如 EventHandler)。
  2. Func 委托

    • LINQ 查询中大量使用(例如 .Where(x => x > 5),这里的 lambda 表达式就是一个 Func<T, bool>)。
    • 任何需要传递或返回一个计算逻辑的场景。
  3. Action 委托

    • 用于执行一个操作。例如,启动一个新线程 Task.Run(() => { /* 做某些事 */ })
    • 执行回调方法,通知某件事已完成,但不需要返回结果。

简单类比

  • 自定义委托:像是自己动手打造一个专门的工具。
  • Func/Action:像是从工具箱里直接拿标准化的通用工具,省时省力。

现代C#开发中,除非必要(如事件),应优先使用 FuncAction,它们极大地减少了代码量,提高了开发效率。


通过一些实际开发中的复杂场景来深入理解委托、FuncAction的用法。

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>();

关键总结

  1. 自定义委托:主要用于事件和需要明确语义的场合
  2. Action:用于执行操作,无返回值,适合回调、事件简化版
  3. 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}");}
}

详细解释

  1. 中间件委托签名
Func<MiddlewareContext, Func<Task>, Task>

第一个参数: MiddlewareContext - 上下文对象,用于在中间件之间传递数据

第二个参数: Func - 下一个中间件的委托(next)

返回值: Task - 异步操作任务

  1. 两种构建模式
  • 正向构建 (ExecuteAsync):

    从第一个中间件开始执行

    每个中间件显式调用 await next() 来继续执行

    更灵活,中间件可以决定是否继续执行

  • 反向构建 (ExecuteAsyncReverse):

    从最后一个中间件开始反向包装

    构建完整的调用链后再执行

    更符合ASP.NET Core中间件的模式

  1. 中间件执行流程
请求 → 中间件1 → 中间件2 → 中间件3 → 处理核心逻辑
响应 ← 中间件1 ← 中间件2 ← 中间件3 ← 处理结果
  1. 关键特性
  • 异步支持: 所有中间件都支持异步操作

  • 上下文共享: 通过 MiddlewareContext 共享数据

  • 灵活控制: 中间件可以决定是否继续执行管道

  • 可扩展性: 可以轻松添加新的中间件

  • 错误处理: 可以通过异常处理中间件统一处理错误

  1. 实际应用场景
  • 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() 是中间件管道中的核心机制,它表示:“执行管道中的下一个中间件”。

具体作用:

  1. 控制流转:将执行权传递给下一个中间件
  2. 形成调用链:构建完整的中间件处理流水线
  3. 支持双向处理:允许在下一个中间件执行前后添加逻辑

执行流程示例:

// 管道构建顺序
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() 时:

  1. 如果这是第7个中间件,它会调用第8个中间件(如果有)
  2. 如果没有更多中间件,next() 就是一个空操作
  3. 执行流程会继续返回到调用链

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. 管道会按顺序执行所有中间件:1 → 2 → 3 → 4 → 5 → 6 → 您的自定义中间件
  2. 每个中间件中的 await next() 会调用下一个中间件
  3. 最后一个中间件通常不调用 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();});

错误触发和处理的完整流程

  1. 异常抛出:在自定义操作中(如文件读取、JSON解析)可能抛出异常
  2. 本地捕获:可以在当前中间件的 try-catch 中处理特定异常
  3. 全局处理:未被处理的异常会被管道中的异常处理中间件捕获
  4. 流程控制:通过调用或不调用 await next() 来控制管道继续或中断

总结

  1. 可以完全自定义业务逻辑,包括文件IO、JSON处理、数据库操作等
  2. 错误会自动触发,需要在中间件中进行适当的异常处理
  3. 可以通过 try-catch 局部处理或依赖全局异常处理中间件
  4. 调用 await next() 决定是否继续执行后续中间件
  5. 上下文对象(MiddlewareContext) 用于在中间件间传递数据和状态

这种设计让您能够灵活地处理各种业务场景,同时保持良好的错误处理机制。

关键总结

  1. await next() 是中间件管道的核心,控制执行流程
  2. Lambda 内部可以完全自定义任何业务逻辑
  3. 可以在 await next() 前后添加预处理和后处理逻辑
  4. 可以选择不调用 await next() 来中断管道执行
  5. 中间件顺序很重要,执行流程按添加顺序进行

这种模式提供了极大的灵活性,让您可以像组装乐高积木一样构建复杂的处理流程。

http://www.xdnf.cn/news/19796.html

相关文章:

  • 告别手动复制粘贴:C# 实现 Excel 与 TXT 文本文件高效互转
  • 搭建分布式Hadoop集群[2025] 实战笔记
  • SQL分类详解:掌握DQL、DML、DDL等数据库语言类型
  • p049基于Flask的医疗预约与诊断系统
  • 删除⽂件之git
  • 避免侵权!这6个可免费下载字体网站能放心商用
  • 大模型推理加速深度对比:vLLM vs TensorRT-LLM vs ONNX Runtime,谁是生产环境最优解?
  • hot100——第十周
  • linux(cut,sort,uniq ,tr,sed,awk)命令介绍
  • 两个矩形之间的距离 python
  • 互联网大厂Java面试三大回合全解析:从语言特性到性能安全
  • Python数据分析与处理(一):读取不同格式.mat文件的具体方法【超详细】
  • 图解设计模式
  • python - ( js )object对象、json对象、字符串对象的相关方法、数组对象的相关方法、BOM对象、BOM模型中 Navigator 对象
  • Ubuntu中配置JMmeter工具
  • Java 类加载机制(ClassLoader)的必会知识点汇总
  • 当合规成为主旋律,PSP 如何推动链上消费市场迈向新蓝海?
  • MidJourney AI绘图工具测评:支持Discord指令生成图片,含图生图与非商业版权使用功能
  • 零样本视觉模型(DINOv3)
  • 云手机发展:未来的场景变化
  • 【C++】模板(初阶)--- 初步认识模板
  • 三维重建线结构光之重建原理(单线结构光为例)
  • 避坑指南!解决Navicat运行SQL成功但没有表的问题
  • 达梦数据库在大小写不敏感的情况下,如何使查询比较中依旧可以做大小写敏感比较?
  • FFmpeg命令行音视频工具:高效实现格式转换与批量处理,支持音频提取与精准视频剪辑
  • Parasoft C/C++test如何实现开发环境内嵌的安全检测
  • 多工况切换定向:陀螺定向短节 vs 传统陀螺工具,谁的适配性更强?
  • 【单片机day01】
  • 学习React-8-useImmer
  • TDK InvenSense CH201距离传感器