Week 09 - 阶段检查
这一页用来确认 Week 9 有没有真正掌握:数据量变大时能处理,文件能保存,错误能记录,发布后能验收。
本周学习地图
| 阶段 | 先掌握什么 | 最终能写什么 |
|---|---|---|
| 性能 | Stopwatch、耗时记录 | 能定位慢在哪里 |
| 大列表 | 全部数据和可见数据分开 | 能处理大量商品 |
| 文件 | 对话框、路径、取消选择 | 能导入导出文件 |
| 持久化 | JSON 保存和读取 | 能关闭后恢复数据 |
| 发布 | dotnet publish | 能生成发布目录 |
| 日志 | INFO、WARNING、ERROR | 能留下排错线索 |
本周自测项目:可发布的数据工具
从商品管理页面开始补可靠性,至少包含:
- 使用
Stopwatch输出搜索或加载耗时。 AllProducts保存全部数据,VisibleProducts绑定界面。- 支持 JSON 保存和读取。
- 支持选择文件导入或导出。
- 保存失败时显示错误并写日志。
- 用
dotnet publish -c Release生成发布目录。
过关标准:能说明开发运行和发布目录运行有什么不同。
上位机阶段验收:可保存和发布的桌面程序
本周目标: 解决大量数据、配置持久化、导入导出、日志和 Release 发布。
必须亲手完成:
- 10000 条模拟记录仍可滚动
- JSON 设置重启后恢复
- 在未安装 SDK 的目标电脑运行发布包
过关标准: 发布目录完整,异常有日志和用户提示,配置与数据路径明确。
不要只看答案。新建一个空项目重做一次,运行成功后再故意改坏一处并自己排错。
本周流程图
加载全部商品
-> Stopwatch 记录耗时
-> AllProducts 保存完整数据
-> VisibleProducts 绑定界面
-> 文件对话框选择导入或导出路径
-> JSON 保存和读取状态
-> 日志记录错误
-> dotnet publish 生成发布目录前半部分:本周原理地图
Week 9 的主线是:
程序不能只在开发目录里能跑,还要在数据变多、文件读写、异常发生、发布交付时保持可靠。这一周不是新增很多孤立知识点,而是把项目从“功能能跑”推进到“能交付、能恢复、能排查”。
这一周的能力地图
| 能力 | 解决的问题 | 最小判断 |
|---|---|---|
| 性能测量 | 不靠感觉判断慢 | 能用 Stopwatch 打出耗时 |
| 大列表处理 | 数据多时界面不卡 | 能区分全部数据和可见数据 |
| 文件对话框 | 用户选择文件后读写 | 能处理取消、读取、保存 |
| 数据持久化 | 程序关闭后数据不丢 | 能保存 JSON 并读取回来 |
| 打包发布 | 生成可运行目录 | 能执行 dotnet publish |
| 错误日志 | 出错后能排查 | 能写 INFO、WARNING、ERROR |
功能交付的 6 个检查点
一个功能要进入可交付状态,至少要回答 6 个问题:
- 输入是什么。
- 处理规则是什么。
- 输出结果是什么。
- 数据多时会不会一次做太多事。
- 失败时用户看到什么。
- 失败细节记录在哪里。
如果只能回答前 3 个,说明功能只是跑通了主流程。
如果 6 个都能回答,说明功能已经具备基本交付条件。
本周常见错误和修法
| 常见错误 | 为什么有问题 | 修法 |
|---|---|---|
| 没测量就优化 | 不知道慢在哪里 | 先用 Stopwatch 记录耗时 |
| 列表直接绑定全部数据 | 数据一多界面压力大 | 用 VisibleProducts 控制当前显示 |
| 文件对话框取消后继续读 | 没有文件还继续执行 | 取消时直接 return |
| JSON 读取不判空 | 反序列化可能返回空值 | 用 ?? new List<T>() |
| 发布后不运行 | 开发目录会掩盖问题 | 从发布目录启动一次 |
| 日志只写“出错了” | 没有排查线索 | 写清动作、对象、异常 |
本周通过线
达到下面这些,再进入下一周:
- 能写出
Stopwatch耗时代码。 - 能说清
allProducts和VisibleProducts的区别。 - 能用文件对话框打开和保存文本。
- 能把列表保存成 JSON,再读取回来。
- 能执行一次
dotnet publish -c Release -o ./artifacts/publish。 - 能把异常写入日志文件。
- 能写出一份发布验收清单。
Week 9 必须会用的操作
| 主题 | 必须会的写法 |
|---|---|
| 性能 | Stopwatch.StartNew()、ElapsedMilliseconds |
| 大列表 | allProducts、VisibleProducts、Take(100) |
| 搜索 | Where、Contains、刷新可见集合 |
| 文件对话框 | StorageProvider.OpenFilePickerAsync、SaveFilePickerAsync |
| JSON | JsonSerializer.Serialize、Deserialize |
| 文件 | File.ReadAllText、WriteAllText、Exists |
| 发布 | dotnet publish -c Release -o ... |
| 日志 | 时间、级别、动作、异常消息 |
下半部分:阶段检查完整答案
下面这份程序就是 Week 9 的阶段检查。它不是界面程序,而是用控制台把本周主线压缩到一个可运行文件里。
它会检查 5 件事:
- 生成 5000 条数据。
- 只处理首屏 100 条。
- 保存 JSON。
- 读取 JSON。
- 故意制造错误并写日志。
检查题:Program.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
string appFolder = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"Week09Checkpoint");
Directory.CreateDirectory(appFolder);
string jsonPath = Path.Combine(appFolder, "products.json");
string logPath = Path.Combine(appFolder, "app.log");
Task LogAsync(string level, string message, Exception? ex = null)
{
string detail = ex is null
? message
: $"{message}{Environment.NewLine}{ex}";
string line = $"{DateTimeOffset.Now:yyyy-MM-dd HH:mm:ss.fff zzz} [{level}] {detail}{Environment.NewLine}";
return File.AppendAllTextAsync(logPath, line);
}
var watch = Stopwatch.StartNew();
List<Product> allProducts = Enumerable
.Range(1, 5000)
.Select(i => new Product($"Product {i:0000}", i * 1.5m))
.ToList();
List<Product> visibleProducts = allProducts
.Take(100)
.ToList();
await LogAsync("INFO", $"全部商品 {allProducts.Count} 条,当前显示 {visibleProducts.Count} 条");
string json = JsonSerializer.Serialize(visibleProducts, new JsonSerializerOptions
{
WriteIndented = true
});
await File.WriteAllTextAsync(jsonPath, json);
await LogAsync("INFO", $"JSON 保存完成:{jsonPath}");
string loadedJson = await File.ReadAllTextAsync(jsonPath);
List<Product> loadedProducts = JsonSerializer.Deserialize<List<Product>>(loadedJson)
?? new List<Product>();
watch.Stop();
Console.WriteLine("Week 9 阶段检查");
Console.WriteLine($"全部商品数量:{allProducts.Count}");
Console.WriteLine($"当前显示数量:{visibleProducts.Count}");
Console.WriteLine($"读取回来数量:{loadedProducts.Count}");
Console.WriteLine($"耗时:{watch.ElapsedMilliseconds}ms");
Console.WriteLine($"JSON 文件:{jsonPath}");
Console.WriteLine($"日志文件:{logPath}");
try
{
string missingPath = Path.Combine(appFolder, "missing.json");
await File.ReadAllTextAsync(missingPath);
}
catch (Exception ex)
{
await LogAsync("ERROR", "读取 missing.json 失败", ex);
Console.WriteLine("错误检查:已捕获异常并写入日志。");
}
record Product(string Name, decimal Price);运行命令
dotnet new console -n Week09Checkpoint
cd Week09Checkpoint把上面的代码放进 Program.cs,然后执行:
dotnet run正确运行结果
结果里的耗时会因电脑不同而不同,但数量必须一致:
Week 9 阶段检查
全部商品数量:5000
当前显示数量:100
读取回来数量:100
耗时:若干 ms
JSON 文件:...
日志文件:...
错误检查:已捕获异常并写入日志。这份答案检查了哪些能力
| 检查点 | 对应代码 | 合格标准 |
|---|---|---|
| 性能测量 | Stopwatch.StartNew() | 能看到耗时 |
| 大列表处理 | Take(100) | 不一次处理全部显示数据 |
| 持久化 | JsonSerializer.Serialize | 能保存 JSON |
| 数据恢复 | Deserialize<List<Product>> | 能读取回来 100 条 |
| 错误处理 | try/catch | 不让程序直接崩溃 |
| 日志 | LogAsync("ERROR", ...) | 错误细节写入文件 |
发布检查答案
阶段检查还要补一份发布命令和验收清单。
最小发布命令
dotnet build
dotnet publish -c Release -o ./artifacts/publishmacOS Apple Silicon
dotnet publish -c Release -r osx-arm64 --self-contained true -o ./artifacts/osx-arm64Windows x64
dotnet publish -c Release -r win-x64 --self-contained true -o ./artifacts/win-x64发布验收清单
发布目录检查:
1. 发布目录存在。
2. 主程序文件存在。
3. 依赖文件存在。
4. runtimeconfig 文件存在。
启动检查:
1. 从发布目录启动程序。
2. 主窗口能显示。
3. 主流程能执行。
4. 资源和样式没有丢失。
数据检查:
1. 能保存 JSON。
2. 能读取 JSON。
3. 关闭后再打开,数据能恢复。
错误检查:
1. 错误操作不会直接崩溃。
2. 用户能看到简短提示。
3. 日志文件能看到详细异常。
交付检查:
1. 版本号写清楚。
2. 发布日期写清楚。
3. 是否需要 .NET Runtime 写清楚。
4. 目标系统写清楚。最终通过标准
这一页通过时,应该能做到:
- 运行阶段检查程序,数量结果正确。
- 打开 JSON 文件,能看到商品数据。
- 打开日志文件,能看到 INFO 和 ERROR。
- 说清
visibleProducts为什么不是allProducts。 - 说清用户提示和日志的区别。
- 写出至少一条发布命令和一份验收清单。
Week 9 的重点不是“记住某个 API”,而是形成一条完整判断路线:先测量,再控制数据量;要保存,就明确路径;会失败,就捕获并记录;要交付,就从发布目录重新验收。