基于Prism的Revit插件:自动生成对话框名称常量类方案咨询
解决方案与代码异味分析
一、代码生成器的可行性实现
可以通过Roslyn源代码生成器实现需求,针对你提到的两种场景,具体实现思路如下:
1. 检测UI项目新增对话框子类
编写Roslyn分析器扫描UI项目中继承自Prism对话框基类(如IDialogAware、DialogWindow或自定义对话框基类)的类型,自动生成静态类的常量字段:
- 字段名可通过类型名转换生成(比如去掉
Dlg前缀、转为大驼峰格式) - 字段值默认使用类型的
FullName(Prism默认的对话框注册名称),若有自定义名称需求,可通过给对话框类添加特性(如[DialogAlias("CustomName")])来指定
2. 检测RegisterDialog注册调用
扫描项目中所有IContainerRegistry.RegisterDialog<,>()的调用:
- 提取泛型参数中的视图类型,获取其默认注册名称或特性指定的自定义名称
- 若调用的是带自定义名称的重载(如
RegisterDialog<,>("CustomName")),则直接提取字符串常量作为字段值
生成的静态类示例:
public static class DialogNames { public const string UserSettings = "YourUiNamespace.DlgUserSettings"; public const string ProjectConfig = "CustomProjectConfigDialog"; }
二、潜在的代码异味与风险
1. 扫描逻辑的准确性隐患
- 若对话框类未正确继承基类、或通过反射/动态类型调用
RegisterDialog,生成器会漏生成常量,导致运行时抛出DialogNotFoundException - 自定义名称时,若生成器未正确提取注册代码中的字符串常量,会出现常量值与实际注册名称不匹配的问题
2. 跨项目耦合违反依赖倒置原则
抽象层的静态类直接依赖UI层的具体视图类型,打破了“抽象不依赖细节”的设计原则。一旦UI层类型改名、删除,抽象层会直接编译报错,增加跨项目维护成本。
3. 编译性能开销
每次编译都要扫描整个解决方案的代码,大型项目中会明显增加编译耗时;若生成器逻辑复杂,还可能出现编译卡顿。
4. 调试排查难度提升
自动生成的代码无人工编写痕迹,出现常量值错误、重复定义等问题时,需要排查生成器逻辑而非业务代码,增加调试复杂度。
5. 违背Prism的约定优于配置原则
Prism默认以视图类型的FullName作为对话框名称,完全可以通过泛型扩展方法封装ShowDialog,无需硬编码或生成静态类:
public static class DialogServiceExtensions { public static void ShowDialog<TView>(this IDialogService dialogService, IDialogParameters parameters = null, Action<IDialogResult> callback = null) { var dialogName = typeof(TView).FullName; dialogService.ShowDialog(dialogName, parameters, callback); } }
调用时直接写dialogService.ShowDialog<DlgUserSettings>(params),完全规避硬编码,更符合Prism的设计思路。
三、替代方案建议
- 泛型扩展方法封装:如上述示例,直接通过视图类型获取对话框名称,无需额外生成代码
- 特性统一管理名称:给对话框视图添加
[DialogName("CustomAlias")]特性,在扩展方法和注册逻辑中统一读取特性值,保持名称一致性
内容的提问来源于stack exchange,提问作者Samer
相关产品推荐
相关产品推荐

