如何更好地工程化实现通用参数列表?C#桌面GUI开发设计咨询
你现在遇到的矛盾本质是配置所有权的边界划错了:你既想让自定义视图自主决定不同步骤需要什么参数,又想让核心封装的PreparePathViewModel来统一组装、传递每个步骤的配置,两边权责拧在一起,才会出现两个方案都有硬伤的情况。
先直接说你列的两个方案的固有问题:
- 第一种
List<Dictionary<string, object>>的缺陷远不止没有编译期校验:靠列表索引匹配步骤是完全的隐式约定,后续只要调整步骤顺序、增删中间步骤,所有自定义视图读取配置的逻辑会直接错位,而且问题只会在运行时暴露,排查成本极高;字符串key、object类型值带来的类型转换错误、拼写错误,全是无意义的冗余成本,根本不是泛化设计必须付出的代价。 - 第二种
List<IStepSettings>的问题你判断的没错:作为通用封装的维护者,你不可能预知所有自定义视图的配置需求,最后要么IStepSettings膨胀成什么都装的万能容器,退化成第一种字典方案的强类型壳子;要么每接一个新的自定义视图就要改核心层的接口定义,直接违反开闭原则,根本做不到通用API要求的扩展性。
更合理的实现思路是把配置的解析权完全交还给自定义视图,核心流程只负责传递框架层已知的、固定结构的上下文,不要替扩展方做配置拆分的工作:
- 调整
ICustomMecPathViewModel的Reload方法参数,不再传递提前拆分好的步骤配置,直接传递全量执行上下文,示例代码如下:
// 核心层固定的流程上下文结构,只定义框架层负责填充的字段 public record PathExecutionContext( int CurrentStepIndex, StepMeta CurrentStep, // 包含步骤ID、名称、内置固定参数等框架维护的元信息 IServiceProvider ServiceProvider, // 支持自定义视图从DI容器按需取服务 Dictionary<string, object> SharedState // 跨步骤共享的全局状态,兼容你现有的字典传参逻辑 ); interface ICustomMecPathViewModel : IScreen, IMecPathViewModel { Dictionary<string, object> GetStepResult(); // 自定义视图自行从上下文中提取当前步骤需要的配置,核心层不需要关心具体逻辑 void Reload(PathExecutionContext context); }
这种改法下,自定义视图完全可以根据CurrentStep.Id而不是列表索引判断当前步骤,步骤顺序调整不会影响配置读取逻辑;需要什么参数直接从上下文、DI容器拿,不需要核心层提前组装。
- 如果你需要支持调用方在初始化流程时给自定义视图传入专属的强类型配置,可以加一层泛型接口做补充,完全不需要改动核心层的基础逻辑:
// 保留原有的非泛型构造函数,适配不需要专属配置的场景 public PreparePathViewModel( ILogger logger, IConfiguration config, ICustomMecPathViewModel customParameterViewModel, ITool tool, Action<MecPathModel> callback) { // 原有初始化逻辑 } // 泛型重载,支持传入强类型自定义配置 public PreparePathViewModel<TCustomSettings>( ILogger logger, IConfiguration config, ICustomMecPathViewModel<TCustomSettings> customParameterViewModel, TCustomSettings customSettings, ITool tool, Action<MecPathModel> callback) where TCustomSettings : class { // 初始化时直接把专属配置传给视图,核心层不需要感知TCustomSettings的结构 customParameterViewModel.BindCustomSettings(customSettings); // 复用非泛型版本的初始化逻辑 } // 支持强类型配置的扩展视图接口 interface ICustomMecPathViewModel<TCustomSettings> : ICustomMecPathViewModel { void BindCustomSettings(TCustomSettings settings); }
这种实现可以同时兼顾两类需求:简单场景可以直接用非泛型接口,通过共享上下文传参;需要编译期类型校验的场景可以实现泛型接口,传入自己定义的配置类型,核心层永远不需要为了适配新的配置类型修改代码,也不会出现接口堆砌零散定义的问题。
最后纠正一个认知偏差:通用泛化设计要付出的成本,是边界处的适配成本,而不是为隐式约定、运行时错误买单的维护成本。你之前倾向的第一种方案,本质是把核心层本该承担的边界设计成本,转嫁给了所有调用方去填运行时的坑,长期来看维护成本会远高于初期的实现成本。
内容的提问来源于stack exchange,提问作者user19335768
相关产品推荐
相关产品推荐

