C# 简述委托,Func与Action委托。 他们之前有什么区别?

发布于:2025-09-03 ⋅ 阅读:(15) ⋅ 点赞:(0)


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#版本,或需要更清晰语义时(如事件) 代表有返回值的操作 代表执行一个操作(无返回值)
参数 自定义 最后一个是返回类型,前面是输入参数 所有泛型参数都是输入参数

使用场景与建议

  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:执行当前中间件,并调用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}");
    }
}

详细解释

  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);
    
    // 检查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. 管道会按顺序执行所有中间件: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. 中间件顺序很重要,执行流程按添加顺序进行

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


网站公告

今日签到

点亮在社区的每一天
去签到