如何在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
相关产品推荐
相关产品推荐

