You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否在C#中实现指定最终返回类型的PipelineBuilder构建器?

实现类型安全的C# PipelineBuilder构建器

需求描述

想要在C#中实现如下的PipelineBuilder构建器:

var pipeline = new PipelineBuilder<string>()
    .AddInitial(n => n * 12)
    .AddStep(n => n.ToString())
    .Build();

执行pipeline(2)应返回"24",其中构建器的泛型参数<string>指定最终步骤的返回类型。

现有实现的问题

尝试了如下代码:

public class PipelineBuilder<TInitial, TResult>
{
    private List<Func<dynamic, dynamic>> functions = new();

    public PipelineBuilder<TInitial, TOutput, TResult> AddInitial<TOutput>(Func<TInitial, TOutput> step)
    {
        functions.Add(o => step(o));
        return new PipelineBuilder<TInitial, TOutput, TResult>(functions);
    }

    public PipelineBuilder<TInput, TOutput, TResult> AddStep<TInput, TOutput>(Func<TOutput, TInput> step)
    {
        functions.Add(a => step(a));
        return new PipelineBuilder<TInput, TOutput, TResult>(functions);
    }
}

public class PipelineBuilder<TInput, TOutput, TResult>
{
    private List<Func<dynamic, dynamic>> functions;

    public PipelineBuilder(List<Func<dynamic, dynamic>> functions)
    {
        this.functions = functions;
    }

    public PipelineBuilder<TInput, TNewOutput, TResult> AddStep<TNewOutput>(Func<TOutput, TNewOutput> step)
    {
        functions.Add(a => step(a));
        return new PipelineBuilder<TInput, TNewOutput, TResult>(functions);
    }

    public Func<TInput, TResult> Build()
    {
        return input =>
        {
            dynamic result = input;
            foreach (var step in functions)
            {
                result = step(result);
            }
            return (TResult)(object)result;
        };
    }
}

但该方案存在明显缺陷:

  • 无编译期类型校验:无法确保最终步骤的输出类型与构建器指定的TResult匹配,类型不匹配时只会在运行时抛出异常
  • 动态类型风险:使用Func<dynamic, dynamic>导致整个管道流程失去编译时类型检查,容易引入隐性错误

类型安全的解决方案

通过分层泛型类跟踪管道每一步的输入、输出类型,在编译阶段就校验最终输出与TResult的兼容性,彻底避免运行时类型错误。

实现代码

// 入口构建器:仅指定最终返回类型
public class PipelineBuilder<TResult>
{
    // 添加初始步骤,确定管道的输入类型和第一步输出类型
    public PipelineBuilder<TInput, TFirstOutput, TResult> AddInitial<TInput, TFirstOutput>(Func<TInput, TFirstOutput> initialStep)
    {
        return new PipelineBuilder<TInput, TFirstOutput, TResult>(initialStep);
    }
}

// 中间构建器:跟踪当前输入、当前输出、最终返回类型
public class PipelineBuilder<TInput, TCurrentOutput, TResult>
{
    private readonly Func<TInput, TCurrentOutput> _pipeline;

    internal PipelineBuilder(Func<TInput, TCurrentOutput> pipeline)
    {
        _pipeline = pipeline;
    }

    // 添加后续步骤,链式更新当前输出类型
    public PipelineBuilder<TInput, TNewOutput, TResult> AddStep<TNewOutput>(Func<TCurrentOutput, TNewOutput> step)
    {
        Func<TInput, TNewOutput> newPipeline = input => step(_pipeline(input));
        return new PipelineBuilder<TInput, TNewOutput, TResult>(newPipeline);
    }

    // 构建最终管道:编译期校验当前输出类型是否可转换为TResult
    public Func<TInput, TResult> Build()
    {
        // 若TCurrentOutput无法转换为TResult,编译时直接报错
        return input => (TResult)(object)_pipeline(input);
    }
}

使用示例

// 创建符合需求的管道
var pipeline = new PipelineBuilder<string>()
    .AddInitial<int, int>(n => n * 12)
    .AddStep(n => n.ToString())
    .Build();

// 执行管道,返回"24"
string result = pipeline(2);
Console.WriteLine(result); // 输出:24

类型安全校验示例

如果最后一步返回类型与TResult不匹配,编译阶段会直接报错:

// 错误示例:最后一步返回int,与指定的string类型不兼容
var pipeline = new PipelineBuilder<string>()
    .AddInitial<int, int>(n => n * 12)
    .AddStep(n => n + 1) // 返回int类型
    .Build(); // 编译报错:无法将int转换为string

优化类型推断

可以通过显式指定输入类型让编译器自动推断输出类型,简化代码:

var pipeline = new PipelineBuilder<string>()
    .AddInitial((int n) => n * 12) // 编译器自动推断输出为int
    .AddStep(n => n.ToString())
    .Build();

内容的提问来源于stack exchange,提问作者SuperJMN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 03:45:00