如果你常年写 WPF,一定遇到过这种场景:一个 TextBlock 放在 StackPanel 里怎么都不换行,明明已经设置了 TextWrapping="Wrap";一个 DataGrid 想做"勾选行 + 批量删除",绑定来绑定去就是要么选不中、要么删不掉;一个 ComboBox 下拉框底部莫名其妙多出一整条空白,翻遍属性也找不到原因。这些放在 Web 前端三分钟能解决的问题,在 WPF 里往往意味着翻 MSDN、查 Stack Overflow、复制一段改半天才能用的代码。从去年开始,我所在的团队在 .NET 9 + WPF 的企业级项目里深度引入了 AI 辅助开发,情况发生了变化。但变化并不只是"写代码变快了"这么简单,它正在重塑我们整个企业级应用架构的设计方式、代码分层逻辑,甚至代码评审的流程。
这篇文章不聊概念,只聊我在真实项目里踩过的坑、验证过的方法、以及最终沉淀下来的架构约定。如果你也在做 WPF 企业级开发,或者正在思考 AI 辅助开发到底能帮 .NET 桌面端团队做到什么程度,这篇文章应该能给你一些直接可用的参考。
1. AI 辅助开发让 WPF 企业级开发从"拼凑代码"转向"说清楚需求"
1.1 WPF 开发的老大难:为什么一个简单界面要写这么多代码
WPF 是一个上限很高、下限也很低的 UI 框架。它的数据绑定、依赖属性、路由事件、模板系统,设计得非常完整,完整到很多功能你根本用不上;但一旦用上,它的繁琐程度也是惊人的。
在企业级项目里,最耗时间的往往不是业务逻辑本身,而是那些"没什么技术含量但又不得不写"的代码。比如一个标准的列表页面,你需要定义数据模型、编写 ViewModel、设计 XAML 的 DataGrid 列、配置样式资源、写增删改查命令。光是一个 DataGridTextColumn 的绑定,就涉及 Binding Path、UpdateSourceTrigger、StringFormat、IsReadOnly 这些属性。如果列表里还要有 ComboBox、CheckBox、DatePicker,代码量直接翻倍。
在过去,我们的开发流程是:遇到问题,去搜索引擎查,然后从别人的代码片段里复制、改造、调试。这个流程有两个问题。第一,很多 WPF 的坑是上下文相关的,别人的解决方案不一定适用于你的业务场景,经常需要二次研究。第二,Stack Overflow 上的答案是碎片化的,缺少完整的架构上下文,一个初学者很容易把代码拼出"能跑但不可维护"的样子。
1.2 AI 改变了开发模式的本质:从"我来写"到"我来判断"
引入 AI 辅助开发之后,最直观的变化是让 AI 承担"写那些样板代码"的工作,而开发者把精力放在"判断哪些代码是对的、是否适合当前场景"上。
打个比方,以前写 WPF 的 MVVM 命令,就像每个按钮都要现场写一段电话接线员的转接逻辑:哪里来、去哪里、怎么触发。现在这些标准代码,AI 能在一秒钟内生成,而且几乎不会出错。你需要做的,是告诉 AI"我要一个删除选中行的命令,要求有确认提示、删除后刷新列表",然后把生成的代码放进你的 ViewModel 里检查一遍。
但这里有个关键变化:过去的编程难点在于"怎么写",现在的难点在于"怎么描述清楚"。你用什么样的措辞描述需求,AI 返回的代码质量就有很大差异。这恰恰是很多团队忽视的点——他们以为 AI 辅助开发就是让 AI 写代码,却不知道在一个企业级 WPF 项目里,真正拉开效率差距的是你的需求描述能力和架构判断力。
1.3 效率提升的真实感受:一个表单页面从半天到半小时
说一个我自己的实测数据。我们有个业务模块需要在 WPF 里做一个包含基础信息、明细列表、附件上传三个区域的表单页面。过去用传统方式,不算业务接口联调,光把 XAML 和 ViewModel 的骨架搭出来,一个中级开发者的速度大约是半天。现在用 AI 辅助开发,先把需求拆成几个描述片段,让 AI 分别生成 XAML 布局、ViewModel 属性、命令骨架,然后拼装、调整、处理几个特殊交互逻辑,整个过程控制在一小时内。
这背后其实有一个很重要的逻辑:WPF 的代码非常适合 AI 生成,因为它的框架约束很强。XAML 有固定的语法结构,MVVM 有固定的分层模式,DataTemplate、Style、Trigger 这些都是高度模式化的内容。对于这种模式化极强、组合方式又复杂的框架,AI 大模型在训练数据里见过足够多的范例,生成的质量往往比刚入门的开发者手写还要规范。
不过这里也要泼一盆冷水:AI 生成代码很快,但如果你不知道怎么把它放进正确的架构位置,它就是在给架构埋雷。这就是我在后面章节要重点讲的——AI 能帮你写代码,但架构的边界必须由人来划。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. .NET 9 + WPF:这个组合在 AI 时代为什么更值得深耕
2.1 .NET 9 给 WPF 带来的实际改进
先说一个背景:.NET 9 是 STS(标准期限支持)版本,不是 LTS,上一个 LTS 是 .NET 8,下一个 LTS 是 .NET 10。所以如果你的企业合规要求很严,可能会倾向于继续留在 .NET 8。但 .NET 9 本身给 WPF 带来的改进,值得认真看一眼,因为升级成本几乎为零,收益却是实打实的。
首先是运行时层面的改进。.NET 9 对 RyuJIT 做了大量优化,特别是针对大型方法的内联和分层编译的改进。WPF 应用是典型的 UI 密集型应用,启动时要做大量 XAML 解析和类型初始化,.NET 9 在这些场景下比 .NET 8 有可感知的提升。我们项目的客户端启动时间,在升级到 .NET 9 后大约缩短了 6% 到 8%,内存占用也有轻微下降。
其次是 Hot Reload 的完善。这个功能对 AI 辅助开发来说至关重要。AI 生成一段 XAML 或 C# 代码后,你可以不重启应用直接看到效果,快速迭代、验证、修正。过去我在 WPF 里改一个样式,要重新编译、启动、登录、打开页面,来回一趟两分钟。现在 Hot Reload 加上 AI 生成,改一个界面细节的时间被压缩到十几秒,整个开发节奏完全不一样了。
还有一点容易被忽略:.NET 9 让 WPF 在 Windows 10/11 上的兼容性和稳定性更好了。企业级应用最怕的不是功能少,而是运行环境五花八门。Windows 企业环境中还有大量 Win10 1809、LTSC 版本的机器,WPF 在这方面的适配度是很多新框架比不了的。
2.2 一个必须说清楚的限制:WPF 不支持 Native AOT
每次聊到 .NET 9,总有人问 WPF 能不能用 Native AOT。答案是:截至 .NET 9,WPF 仍然不支持 Native AOT。这是 WPF 的底层架构决定了的,因为 WPF 大量使用反射和动态代码生成,跟 AOT 的静态分析逻辑相冲突。
这个限制对企业级应用有影响吗?其实影响不大。企业级 WPF 应用通常跑在内网环境,对启动速度的要求远没有对稳定性和可维护性的要求高。Native AOT 主要对云原生、微服务、命令行工具这些场景更有价值。真正需要关注的是,你在做架构决策时要知道这一点,避免在项目汇报里给领导一个不切实际的预期。
2.3 为什么企业级应用依然信任 WPF
客观说,.NET 生态里不是没有其他 UI 方案。Avalonia 也在快速成长,MAUI 是微软主推的跨平台方案。但放到企业级 Windows 桌面应用这个具体场景,WPF 的地位依然稳固。
原因有三个。第一,生态成熟度。WPF 从 2006 年推出至今,积累了海量的第三方控件库、开源项目、企业级解决方案。Prism、LiveCharts2、DevExpress、Telerik,这些库在企业应用里久经考验。AI 模型对 WPF 的知识覆盖面也是最广的,这意味着 AI 辅助开发在 WPF 领域的表现远比其他桌面框架更好。
第二,团队技能栈。很多企业的核心业务系统已经用 WPF 运行了七八年甚至十几年,团队对 WPF 的熟练度远高于新框架。引入 AI 辅助开发之后,这个技能栈不但没有被淘汰,反而因为效率提升而变得更加有价值。
第三,Windows 桌面业务场景的稳定性需求。生产制造、医疗、金融、物流这些行业,客户端软件的稳定性是第一位的。WPF 的数据绑定和 MVVM 架构经过这么多年的实践验证,踩坑的规律基本都被摸透了。AI 的加入,其实是给这套成熟体系加了一个"效率杠杆",而不是要推翻它重来。
3. 企业级 WPF 开发中,AI 真正帮我解决的硬骨头
这一章我挑几个我在实际项目中用过 AI 辅助解决的典型问题,把 AI 给出的方案、我做的修正、以及背后的原理讲清楚。这些问题的共同点是:它们看起来都是"小问题",但在企业级项目里出现的频率极高,而且按照传统方式解决,每次都要花不少时间。
3.1 DataGrid 行内勾选、点击按钮批量删除的 MVVM 实现
先说这个最常见的需求:DataGrid 每行前面有一个 CheckBox,用户勾选多行之后,点击页面上的"删除选中"按钮,批量删除这些行,而且全程不能在 Code-Behind 里写业务逻辑。
早期我让 AI 直接生成代码,它给了我一个很常见的实现:在 ViewModel 里用 SelectedItems 属性加一个 ICommand,通过按钮的 Command 绑定来删除。这个思路看着对,但在 WPF 里 DataGrid.SelectedItems 是只读的,无法直接绑定到 ViewModel,除非用行为(Behavior)或者绕过绑定。AI 生成的第一版代码能编译,但运行时绑不上。
后来我把需求描述得更精确:每一行的 ViewModel 需要有一个 IsSelected 属性,DeleteCommand 从 Items 集合里移除所有 IsSelected == true 的行。AI 立刻给出了正确的 MVVM 实现。
csharp复制public class ItemViewModel : ViewModelBase
{
private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
set => SetProperty(ref _isSelected, value);
}
public string Name { get; set; }
}
public class MainViewModel : ViewModelBase
{
public ObservableCollection<ItemViewModel> Items { get; }
public ICommand DeleteSelectedCommand { get; }
public MainViewModel()
{
Items = new ObservableCollection<ItemViewModel>();
DeleteSelectedCommand = new RelayCommand(DeleteSelected, CanDeleteSelected);
}
private void DeleteSelected()
{
var selected = Items.Where(i => i.IsSelected).ToList();
foreach (var item in selected)
{
Items.Remove(item);
}
}
private bool CanDeleteSelected()
{
return Items.Any(i => i.IsSelected);
}
}
XAML 部分,关键是 DataGrid 列里 CheckBox 的绑定。用 DataGridCheckBoxColumn 有个坑:它的默认绑定在某些情况下不会立即更新源,导致你勾选了行但 ViewModel 里的 IsSelected 还是 false。稳妥的做法是用 DataGridTemplateColumn 显式放一个 CheckBox,并设置 UpdateSourceTrigger=PropertyChanged:
xml复制<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="选择">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding Name}" Header="名称" />
</DataGrid.Columns>
</DataGrid>
<Button Content="删除选中行" Command="{Binding DeleteSelectedCommand}" />
这里有个值得注意的架构细节:多选删除的关键是"行模型承载选中状态",而不是用 DataGrid 的 SelectedItem。因为 SelectedItem 只能单选,即使配合 SelectedItems 也需要绕道 Code-Behind。做企业级应用时,这种设计决策必须由人来判断,AI 只能根据你给的约束提供符合要求的代码。
3.2 ComboBox 下拉框末尾出现空白:AI 辅助排查的典型路径
这个问题的现象很怪:ComboBox 下拉列表的每一项数据都正常,但列表最底部有一整条空白区域,怎么点都选不中任何东西,也不报错。以前遇到这种问题,开发者一般会花很长时间检查 ComboBox 的属性设置。用 AI 辅助排查,排查效率会高很多。
我把截图和 XAML 描述给 AI,它列出了四个可能原因:
ItemsSource绑定的集合中是否存在null项,null在 ComboBox 里会渲染成空白行。SelectedValuePath设置后,集合中的某项的关联值为空,导致无法正确显示。MaxDropDownHeight设置过大,在有滚动条时出现底部空余区间。- ItemsSource 绑定的对象集合里,某些对象的
ToString()返回空字符串或空格。
我逐一排查后,发现真正的原因是第 4 种:集合里有一条脏数据,显示文本字段是一串空白字符。这个问题用传统方式排查,可能要调试很久,因为它在 UI 上呈现为一个空白项,你不会第一时间想到是数据源的问题。AI 能快速列出排查路径,但你需要对数据流和绑定机制有基本理解,才能按顺序排查。
顺带说一个 ComboBox 在 MVVM 场景下最常见的坑:SelectedValue 绑定要在 SelectedValuePath 设置之后才生效,而且初始化顺序错了可能导致界面显示空白。这个问题用 AI 生成代码时经常出现,因为 AI 倾向于给出最简短的绑定写法。
xml复制<ComboBox ItemsSource="{Binding StatusOptions}"
SelectedValuePath="Value"
SelectedValue="{Binding CurrentStatus}" />
如果 StatusOptions 是新建的集合,而 CurrentStatus 在 ViewModel 构造时设置得过早,视图还没完成绑定,就会出现选中状态丢失的现象。解决方案是确保 SelectedValue 的设置发生在 Loaded 事件之后,或者通过 Dispatcher.BeginInvoke 延迟赋值。
3.3 StackPanel 内 TextBlock 不换行:AI 给你方案,但你要懂布局原理
这个题目看起来简单,实际上坑很深。一个 TextBlock 放在垂直方向的 StackPanel 里,你设置了 TextWrapping="Wrap",但它就是不换行。AI 会直接告诉你加 TextWrapping="Wrap",然后你试了发现没用,再问它,它才会提起 MaxWidth 或改用 Grid。
为什么 TextWrapping 在 StackPanel 里无效?因为 WPF 的布局系统分两个阶段:测量(Measure)和排列(Arrange)。垂直方向的 StackPanel 在测量时,会告诉子元素"你想要多宽就给多宽",水平方向是无限空间。TextBlock 收到一个无限大的可用宽度,自然认为不需要换行。所以无论 TextWrapping 设置成什么,它都会按一行显示。
解决方案有三种:
xml复制<!-- 方案一:用 Grid 代替 StackPanel,让可用宽度受限于实际宽度 -->
<Grid>
<TextBlock Text="{Binding Description}" TextWrapping="Wrap" />
</Grid>
<!-- 方案二:显式限制 MaxWidth -->
<StackPanel>
<TextBlock Text="{Binding Description}" TextWrapping="Wrap" MaxWidth="300" />
</StackPanel>
<!-- 方案三:给 TextBlock 一个固定宽度 -->
<StackPanel>
<TextBlock Text="{Binding Description}" TextWrapping="Wrap" Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=StackPanel}}" />
</StackPanel>
这里我想说的重点不是方案本身,而是 AI 辅助开发的一个边界:AI 能给出一个在大多数场景下正确的方案,但你如果不知道布局系统的底层原理,就只能停留在"试了不行再试下一个"的泥潭里。在企业级项目中,这种对原理的理解恰恰是架构师的不可替代性所在。
3.4 大量动态创建的 Button:从 Code-Behind 循环到模板化架构
另一个高频需求:界面上需要根据数据动态生成大量按钮,每个按钮对应不同的操作指令。在没有 AI 之前,很多 WPF 开发者的第一反应是在 Code-Behind 里写循环:
csharp复制for (int i = 0; i < operations.Count; i++)
{
var btn = new Button
{
Content = operations[i].Name,
Tag = operations[i]
};
btn.Click += OperationButton_Click;
OperationPanel.Children.Add(btn);
}
这段代码能跑,但放在企业级应用里是灾难:事件订阅导致内存泄漏风险、无法样式化、无法做权限控制、ViewModel 无法测试。
用 AI 辅助开发时,我直接把需求和架构约束一起发给 AI:"用 MVVM 实现动态按钮列表,按钮数据来自 ViewModel 的 ObservableCollection,点击按钮执行对应命令并传入当前数据对象,禁止在 Code-Behind 中写事件处理。"AI 给出的方案就完全是模板化的思路:
xml复制<ItemsControl ItemsSource="{Binding OperationButtons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding DisplayName}"
Command="{Binding DataContext.OperationCommand, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
这个案例很好地展示了 AI 辅助开发的正确用法:你给 AI 的约束越接近企业级架构规范,它返回的代码就越接近你想要的。反过来,如果你只是在聊天窗口里说"帮我写一段 WPF 动态创建按钮的代码",它大概率会给你第一种循环创建的 Code-Behind 版本。
3.5 LiveCharts2 和 Prism 的集成:第三方框架场景下 AI 的可靠与不靠谱
LiveCharts2 是 WPF 里做图表比较主流的库,但它的 API 和旧版 LiveCharts 完全是两代设计。旧版用 SeriesCollection、CartesianChart,新版用 ISeries[]、Axis[],命名完全不同。AI 的训练数据里两代代码混杂,很容易给出过时的示例。
我在集成 LiveCharts2 时,让 AI 生成图表配置代码,它第一版给的是旧版 API,根本无法编译。我补充了一句"使用 LiveChartsCore.SkiaSharpView.WPF 命名空间",它才纠正为:
csharp复制using LiveChartsCore;
using LiveChartsCore.SkiaSharpView;
using LiveChartsCore.SkiaSharpView.WPF;
public class DashboardViewModel
{
public ISeries[] Series { get; set; }
public Axis[] XAxes { get; set; }
public Axis[] YAxes { get; set; }
public DashboardViewModel()
{
Series = new ISeries[]
{
new LineSeries<double>
{
Values = new double[] { 12, 18, 24, 30, 28, 35 },
Name = "月度产量"
}
};
XAxes = new Axis[]
{
new Axis { Labels = new[] { "1月", "2月", "3月", "4月", "5月", "6月" } }
};
YAxes = new Axis[]
{
new Axis { MinLimit = 0 }
};
}
}
XAML 里还要记得在 App.xaml 中引入 LiveCharts 的主题资源,不然图表控件没有默认样式:
xml复制<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/LiveChartsCore.SkiaSharpView.WPF;component/Theme/LiveCharts.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
这个案例给我的经验是:使用第三方库时,AI 的版本知识往往会滞后,你需要自己在代码里显式标注版本上下文,并要求它基于当前版本文档回答。这比直接信任 AI 的"记忆"可靠得多。
Prism 框架的集成同理。Prism 的模块化设计、Region 导航、ViewModelLocator 这些概念对于 AI 训练数据来说非常常见,生成样板代码的质量很高。但 Prism 在不同版本里也有不小差异——比如 DryIoc 和 Unity 容器的支持,旧版 Prism 7 用的是 Unity,新版 Prism 8/9 默认用 DryIoc。让 AI 生成模块注册代码时,必须明确指出你用的是哪个版本,否则它可能给你一套基于旧版的代码。
4. AI 生成 WPF 代码最容易翻车的四个场景
我前面讲了很多 AI 能高效处理的场景,但这章必须写给 AI 辅助开发泼冷水的部分。因为企业级应用里,AI 翻车的代价不是重新写一遍代码那么简单,而是可能把安全隐患、性能问题带进生产环境。
4.1 MVVM 分层被 AI 悄悄打破
AI 最擅长从你的描述中推断意图,但它的推断往往是"局部正确"的。我遇到过最典型的问题:让 AI 生成"保存成功后提示用户"的代码,它在 ViewModel 里直接写了 MessageBox.Show("保存成功")。单独看这段代码,功能没错;但放在 MVVM 架构里,这就是一个破坏分层的行为。
ViewModel 不应该直接引用任何 View 相关的类。一旦 ViewModel 里出现 MessageBox、Dispatcher、Window,就意味着这个 ViewModel 无法单元测试,也无法在 View 层切换时复用。正确的做法是通过消息服务或对话框服务来解耦:
csharp复制public interface IDialogService
{
void ShowMessage(string message);
}
// ViewModel 只依赖抽象
public class SaveCommand : ICommand
{
private readonly IDialogService _dialogService;
public void Execute(object parameter)
{
// 业务逻辑
_dialogService.ShowMessage("保存成功");
}
}
AI 为什么容易犯这个错?因为它是从大量训练样本中学习到的模式,而在 Stack Overflow 等社区问答里,直接在 ViewModel 里弹窗的代码太常见了。这提醒我们:AI 生成代码后,架构审查不是可选项,而是必选项。
4.2 加密解密等安全场景,AI 会给出"能用但不安全"的代码
WPF 企业级应用经常涉及敏感数据的加密存储和传输。AI 在生成加密代码时,倾向于使用最简单、最广为人知的实现,而这往往就是最不安全的方式。
比如让 AI 生成一个字符串加密方法,它第一版通常会给出使用 DES 或 MD5 的实现,或者硬编码一个密钥在代码里。这些在技术上是"能跑的",但在企业级安全审计中是完全不合格的。更隐蔽的是,AI 可能会在 AES 加密中使用 ECB 模式、不正确的填充、不安全的密钥派生方式,这些代码在功能测试时看起来正常,但在渗透测试时会被一举击破。
我的处理方式是:核心安全模块,绝不直接使用 AI 生成的代码,只用它做辅助参考,加密算法的选型、密钥管理方案、加盐策略全部由团队里的安全负责人确定,代码实现也要经过严格的代码评审。
4.3 大数据量 DataGrid:AI 不懂你的性能边界
WPF 的 DataGrid 在一个列表只有几十条数据时,性能和功能都没问题。但企业级应用里,一个库存台账、一个操作日志、一个报文查询,轻松就是几千上万条数据。这时候,DataGrid 的默认行为会导致严重的卡顿。
AI 生成的 DataGrid 代码,通常不会考虑虚拟化问题。它不会主动给你的 DataGrid 添加 VirtualizingStackPanel.IsVirtualizing="True",也不会考虑关闭不必要的列操作、排序、冻结列等功能来提升性能。你需要自己明确地告诉 AI:"这个列表可能有 1 万条数据,请考虑虚拟化和性能优化方案。"
此外,大量数据的集合操作也有坑。ObservableCollection 在每次增删时都会触发 UI 通知,如果你一次性往里面 Add 一万条数据,UI 会卡死。正确的做法是使用支持批量更新的集合,比如在构造时传入 List 的 RangeObservableCollection,或者暂时挂起通知。这些性能问题的根因分析,远远超出了 AI 的能力范围——它不知道你的数据规模,也不可能替你做基准测试。
4.4 版本幻觉:AI 混淆 .NET Framework 和 .NET 9 的 API
AI 大模型的知识有截止时间,而 .NET 生态的更新很快。我在使用 AI 辅助开发时,最常遇到的问题就是它给出 .NET Framework 4.x 时代的 API。比如 HttpWebRequest 而不是 HttpClient,Task.Factory.StartNew 而不是 await async,BinaryFormatter(在 .NET 9 里默认不可用)而不是 System.Text.Json。
这个问题在 LiveCharts2、Prism、甚至 WPF 本身的某些 API 上都有体现。我在生成代码时,会在请求里明确加上".NET 9 环境下""WPF for .NET (Core)""不要使用 .NET Framework 4.x API"这样的限定词,能有效减少版本幻觉。
但更深层的问题是,有时候 AI 会给出一个"看起来合理但实际不存在"的 API。这种情况没法通过限定词完全避免,只能通过编译验证和代码评审来兜底。所以我的建议是:AI 生成代码后,第一件事就是编译,第二件事就是让团队成员用企业规范做评审。
5. AI 时代企业级 WPF 架构的四个新约定
AI 辅助开发不是简单地"多了一个写代码的助手",它带来的是一种新的生产方式。生产方式变了,架构约定也必须跟着变。我们团队在实践中逐步沉淀了四条约定,供你参考。
5.1 分层边界:AI 只负责 View 和 ViewModel 的样板代码
我们团队内部定了一条线:AI 可以自由生成 View(XAML)和 ViewModel(属性、命令、集合操作)中的样板代码,但 Model 层、Service 层、以及任何涉及安全、持久化、外部接口的业务代码,必须由人工编写或至少由人工完全重写。
原因很简单。View 和 ViewModel 的代码模式化程度高,AI 的错误率低;即使出错,影响范围也局限在 UI 层,通过界面验证很快能发现。而 Model 和 Service 层的代码往往承载着核心业务规则,一个逻辑判断的错误可能在特定的业务流程里才暴露,排查成本极高。让 AI 越界进入核心业务层,等于接受一个隐藏的定时炸弹。
5.2 提示词资产化:把团队规范写进 AI 提示词
这是我觉得收益最大、但对很多团队来说最陌生的一条。企业级 WPF 项目通常有成文的编码规范,比如命名规则、异常处理方式、通知属性写法、命令接口风格。与其每次让 AI 生成代码后再人工修正,不如把这些规范写进提示词模板,形成团队级别的"提示词资产"。
我们团队整理了一份 WPF 开发需求模板,包含固定字段:技术栈、目标框架、 MVVM 约定、代码风格要求、需要避免的写法、相关类名和接口。开发者在向 AI 提需求时,先复制这个模板,只替换业务描述部分。这个做法在团队内推行后,AI 生成代码的一次通过率大幅提升,代码评审的负担明显下降。
5.3 架构守护自动化:用 Analyzer 和架构测试约束 AI 输出
靠人工评审来阻止 AI 破坏架构边界,是必要的,但还不够。企业级项目还应该引入自动化的架构守护机制。
一个实用的组合是:
- StyleCop Analyzers 或自定义 Roslyn Analyzer,强制命名规范、禁止 ViewModel 引用 View 命名空间。
- 架构约束测试(比如 NetArchTest),在 CI 中检查依赖方向:View 依赖 ViewModel,ViewModel 依赖 Model/Service,禁止反向依赖。
- 单元测试覆盖 Command 的关键逻辑,防止 AI 生成的代码逻辑缺漏。
这套机制不是为了防 AI,而是为了防所有代码——只是 AI 让代码的生产速度变快了,自动化的审查也必须跟上。当你是手写代码时,一天写 300 行,人眼审阅还来得及;当 AI 辅助开发把速度提到一天 1500 行时,没有自动化审查就是在裸奔。
5.4 代码评审的新协议:AIGC 代码的审查清单
最后一条是代码评审流程的变化。我们团队现在对 AI 生成的代码,有一套专门的重点检查项:
- 是否遵守了我们在提示词里约定的分层边界?
- 是否引入了不需要的命名空间或过时的 API?
- 是否存在资源的非托管泄漏(比如 BitmapImage、FileStream 未释放)?
- 异常处理是否符合团队规范,还是被 AI 静默吞掉了?
- 是否有隐藏的线程问题(比如在 UI 线程外操作 ObservableCollection)?
- 硬编码的字符串、密钥、连接字符串是否被意外引入?
这比传统的代码评审多了几个检查维度,但也让评审的聚焦点更清晰。AI 生成代码的速度越快,评审清单的重要性就越高。这不是对 AI 的不信任,而是对企业级工程质量的基本尊重。
我在实际项目里最大的体会是:AI 辅助开发就像一面放大镜,它放大你的架构能力和团队规范——如果架构分层清晰、代码约定明确,AI 会让这套体系如虎添翼;如果本身架构混乱、规范缺失,AI 只会更快地制造出一堆代码垃圾来淹没你。所以,与其焦虑 AI 会不会取代 WPF 开发者,不如现在就把团队的分层边界、提示词约定、自动化审查这三件事做好。等到 .NET 10 出来,AI 能力更强的时候,这些准备工作都会变成你的护城河。
