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扩展
在实际复杂开发中,这些委托类型让代码更加灵活、可扩展和可测试,是实现许多设计模式的关键工具。
开发中的实际调用
下面是一个完整的中间件实现,使用 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:执行当前中间件,并调用next
pipeline = () => 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);
// 检查token
if (!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()
来中断管道执行 - 中间件顺序很重要,执行流程按添加顺序进行
这种模式提供了极大的灵活性,让您可以像组装乐高积木一样构建复杂的处理流程。