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

如何在C#中实现泛型父子结构的转换器链API?

在C#中实现泛型链式异步转换器的统一存储方案

针对你遇到的泛型转换器无法统一存储的问题,核心思路是通过非泛型接口适配绕过C#泛型系统的限制——C#不支持Java式通配符,但可以用非泛型基类/接口作为所有泛型转换器的共同类型,实现统一存储与调用。

1. 定义非泛型异步转换器接口

先创建一个非泛型接口,作为所有泛型转换器的统一契约:

public interface IAsyncConverter
{
    Task<object> ConvertAsync(object input);
}

2. 实现泛型转换器类

让泛型转换器实现上述非泛型接口,在接口方法内部做类型校验与泛型转换:

public class Converter<TIn, TOut> : IAsyncConverter
{
    private readonly Func<TIn, Task<TOut>> _conversionLogic;

    public Converter(Func<TIn, Task<TOut>> conversionLogic)
    {
        _conversionLogic = conversionLogic;
    }

    public async Task<object> ConvertAsync(object input)
    {
        // 校验输入类型,避免运行时错误
        if (input is not TIn typedInput)
            throw new ArgumentException($"输入类型必须为 {typeof(TIn).Name}", nameof(input));
        
        return await _conversionLogic(typedInput);
    }
}

3. 构建链式调用的转换器链

创建ConverterChain类,用List<IAsyncConverter>统一存储所有步骤,并提供链式添加方法:

public class ConverterChain
{
    private readonly List<IAsyncConverter> _converters = new();

    // 链式添加泛型转换器
    public ConverterChain Add<TIn, TOut>(Func<TIn, Task<TOut>> conversionLogic)
    {
        _converters.Add(new Converter<TIn, TOut>(conversionLogic));
        return this;
    }

    // 执行整个链,支持中途插入用户选择逻辑
    public async Task<object> ExecuteAsync(object initialInput)
    {
        var currentValue = initialInput;
        foreach (var converter in _converters)
        {
            currentValue = await converter.ConvertAsync(currentValue);
            
            // 插入用户选择逻辑:比如等待用户确认当前结果后继续
            // 示例:await PromptUserForSelectionAsync(currentValue);
        }
        return currentValue;
    }

    // 可选:强类型返回的重载,避免手动转换
    public async Task<TResult> ExecuteAsync<TResult>(object initialInput)
    {
        var rawResult = await ExecuteAsync(initialInput);
        if (rawResult is not TResult typedResult)
            throw new InvalidCastException($"结果无法转换为 {typeof(TResult).Name}");
        
        return typedResult;
    }
}

4. 使用示例

假设你的实体结构是Enterprise → Worker → Address,可以这样构建并执行链式逻辑:

// 定义实体类
public class Enterprise { public List<Worker> Workers { get; set; } }
public class Worker { public Address Address { get; set; } }
public class Address { public string Street { get; set; } }

// 构建链式调用
var chain = new ConverterChain()
    // 第一步:从Enterprise获取用户选择的Worker
    .Add<Enterprise, Worker>(async enterprise =>
    {
        await Task.Delay(100); // 模拟异步操作
        return enterprise.Workers.First(); // 实际场景替换为用户选择逻辑
    })
    // 第二步:从Worker获取Address
    .Add<Worker, Address>(async worker =>
    {
        await Task.Delay(100);
        return worker.Address;
    })
    // 第三步:对Address执行最终操作(比如提取街道名称)
    .Add<Address, string>(async address =>
    {
        await Task.Delay(100);
        return address.Street;
    });

// 执行链并获取强类型结果
var testEnterprise = new Enterprise 
{ 
    Workers = new List<Worker> { new Worker { Address = new Address { Street = "Main St 123" } } } 
};
var finalResult = await chain.ExecuteAsync<string>(testEnterprise);
Console.WriteLine(finalResult); // 输出:Main St 123

关键说明

  • 这种方案既保留了泛型的类型安全(转换时校验输入类型),又实现了不同泛型转换器的统一存储。
  • 如果需要更复杂的分支逻辑,可以在ExecuteAsync中扩展用户交互逻辑,甚至添加专门用于用户选择的转换器类型。
  • 避免直接使用dynamic存储转换器——虽然可以工作,但会丢失编译时类型检查,增加运行时错误风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 13:13:28