|
| 1 | +# .NET 每周分享第 67 期 |
| 2 | + |
| 3 | +## 卷首语 |
| 4 | + |
| 5 | +社区人员发现,在某些 .NET 官方文档页面中,出现了关于使用 GitHub Copilot 的提示,例如:[自定义属性名称和顺序](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/customize-properties#use-github-copilot-to-customize-property-names-and-order), [使用 Copilot 进行单元测试](https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-copilot)。 他们认为,这些提示可能被视为对 GitHub Copilot 的推广,尤其是在 .NET Foundation 自称为“独立、非营利组织”的背景下。他质疑:如果文档中推广第三方工具(即使是免费的),是否违背了其独立性的承诺?此外,Copilot 并非完全免费,用户可能需要以数据和隐私为代价。他还指出,如果要保持中立,是否也应该在文档中提及其他类似工具? |
| 6 | + |
| 7 | +## 行业资讯 |
| 8 | + |
| 9 | +1、[Microsoft Build 2025](https://devblogs.microsoft.com/dotnet/join-us-at-build-2025/) |
| 10 | + |
| 11 | + |
| 12 | + |
| 13 | +Microsoft Build 2025 将于 5 月 19 日至 22 日在西雅图及线上举行,面向全球开发者开放。.NET 与 C# 团队将带来超过 75 场精彩内容,涵盖 AI、云计算、应用现代化等主题。 |
| 14 | + |
| 15 | +重点议程包括: |
| 16 | + |
| 17 | +- .NET Aspire 与 AI 集成:探索 .NET Aspire 9.3 的新特性,展示如何结合 AI 构建智能应用。 |
| 18 | +- GitHub Copilot 助力应用现代化:介绍如何使用 Copilot 升级 .NET 应用,实现自动修复与依赖分析。 |
| 19 | +- Hanselman 与 Toub 的实战演示:两位专家现场调试代码,深入讲解 .NET 性能优化技巧。 |
| 20 | +- Python 与 .NET 融合:展示如何将 Python 的数据科学能力与 .NET 的企业级开发结合,构建 AI 解决方案。 |
| 21 | + |
| 22 | +无论您是经验丰富的开发者,还是初学者,都能在本次大会中收获丰富的知识与实践经验。欢迎通过线上或线下方式参与,与全球开发者共同交流学习。 |
| 23 | + |
| 24 | +## 文章推荐 |
| 25 | + |
| 26 | +1、[使用反射的四种场景](https://blog.elmah.io/4-real-life-examples-of-using-reflection-in-c/) |
| 27 | + |
| 28 | +反射是 C# 的一项运行时功能,允许程序动态地检查和操作程序集、类型(如类、接口、值类型)、方法、字段和属性的元数据。通过 System.Reflection 命名空间,开发者可以在运行时创建对象实例、调用方法以及加载程序集。 |
| 29 | + |
| 30 | +反射适用于以下场景: |
| 31 | + |
| 32 | +- 需要根据外部输入在运行时加载程序集或类型。 |
| 33 | +- 处理结构未知的 JSON 或 XML 序列化。 |
| 34 | +- 自动将具有相同名称或属性的属性之间的数据进行映射。 |
| 35 | +- 开发依赖注入(DI)容器以解析构造函数、注入参数并激活服务。 |
| 36 | +- 导出数据到 CSV 时,手动映射大量类型的属性不切实际。 |
| 37 | +- 测试工具(如 NUnit 或 xUnit)使用反射发现测试方法和属性,而无需手动注册每个测试用例。 |
| 38 | +- 避免为重复的类型方法和属性编写样板代码。反射可以帮助自动访问属性或调用方法。 |
| 39 | +- 动态访问和验证模型属性。 |
| 40 | + |
| 41 | +常见的方法有 |
| 42 | + |
| 43 | +1. 方法的调用 |
| 44 | +2. 动态 CSV/JSON 的导出 |
| 45 | +3. 自定义类型之间的转换映射 |
| 46 | +4. 动态插件/程序集加载 |
| 47 | + |
| 48 | +虽然反射功能强大,但会带来性能开销。应遵循以下最佳实践: |
| 49 | + |
| 50 | +- 避免在紧密循环中使用反射。 |
| 51 | +- 在性能关键的代码中,避免使用反射。 |
| 52 | +- 重复调用 `GetMethod()` 和 `GetProperty()` 可能代价高昂。使用缓存来存储反射结果。 |
| 53 | +- 使用编译的委托或表达式树来避免重复反射。这可以显著加快性能关键路径中的属性访问或方法调用: |
| 54 | + |
| 55 | +2、[在 System.Text.Json 中使用 Source Generation](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/source-generation) |
| 56 | + |
| 57 | +`System.Text.Json` 的源生成功能(Source Generation)自 .NET 6 起引入,旨在提升 JSON 序列化与反序列化的性能,减少运行时反射的开销。 |
| 58 | + |
| 59 | +使用步骤如下: |
| 60 | + |
| 61 | +- 创建一个继承自 `JsonSerializerContext` 的部分类。 |
| 62 | +- 在该类上应用 `[JsonSerializable]` 特性,指定需要序列化或反序列化的类型。 |
| 63 | +- 在代码中使用 `JsonSerializer` 的方法,并传入生成的上下文实例或类型信息。 |
| 64 | + |
| 65 | +3、[C# 中的模式匹配](https://www.arungudelli.com/csharp-tips/pattern-matching-in-csharp/) |
| 66 | + |
| 67 | +模式匹配是一种机制,允许你将输入值与模式进行比较,并在匹配时执行相应操作。在 C# 中,模式匹配可用于以下结构: |
| 68 | + |
| 69 | +- is 表达式 |
| 70 | +- switch 语句 |
| 71 | +- switch 表达式 |
| 72 | + |
| 73 | +主要使用类型有如下表格 |
| 74 | + |
| 75 | +模式类型 | 最佳使用场景 |
| 76 | +-- | -- |
| 77 | +声明/类型 | 类型检查和转换 |
| 78 | +常量 | 精确值匹配 |
| 79 | +关系 | 范围比较 |
| 80 | +逻辑 | 复杂组合 |
| 81 | +属性 | 匹配对象的属性值 |
| 82 | +位置 | 基于解构的匹配 |
| 83 | +var | 通用值提取 |
| 84 | +丢弃 | 匹配任何内容,忽略值 |
| 85 | +列表 | 匹配数组/列表及子序列 |
| 86 | + |
| 87 | +根据作者的经验,模式匹配在以下情况下表现出色: |
| 88 | + |
| 89 | +- 处理异构类型(例如 object 参数)时 |
| 90 | +- 避免冗长的空值检查。 |
| 91 | +- 使条件语句更具可读性。 |
| 92 | +- 处理复杂的分支逻辑(例如转换 API 响应)时。 |
| 93 | + |
| 94 | +4、[Azure Function 构建 MCP Remote Server](https://devblogs.microsoft.com/dotnet/build-mcp-remote-servers-with-azure-functions/) |
| 95 | + |
| 96 | +本文介绍了如何利用 [Azure Functions](https://learn.microsoft.com/azure/azure-functions/) 构建一个MCP远程服务器,用于处理来自远程设备的请求。它强调了以下关键点: |
| 97 | + |
| 98 | +**背景** |
| 99 | + |
| 100 | +- 某些场景需要与远程设备(如物联网设备)通信。 |
| 101 | +- 这些设备可能不支持现代协议(如 HTTPS 或 gRPC),需要简化的接口。 |
| 102 | +- Azure Functions 是构建轻量级、事件驱动的后端服务的理想选择。 |
| 103 | + |
| 104 | +**架构概述** |
| 105 | + |
| 106 | +- 使用 Azure Functions 提供一个 HTTP 端点,供远程客户端请求。 |
| 107 | +- 结合 Azure Durable Entities 管理设备状态和指令执行。 |
| 108 | +- 结合 Azure Storage 实现数据持久化。 |
| 109 | + |
| 110 | +**示例实现** |
| 111 | + |
| 112 | +- 提供了基于 .NET 的函数模板,包括: |
| 113 | + - `DeviceCommandFunction`: 接收和处理设备命令。 |
| 114 | + - `DeviceStateEntity`: 保存设备状态。 |
| 115 | + - `DurableEntityClient`: 调用实体以管理设备状态。 |
| 116 | + |
| 117 | +**优势** |
| 118 | + |
| 119 | +- 减少基础设施负担:无需管理服务器。 |
| 120 | +- 易于扩展:可自动扩容应对大量设备。 |
| 121 | +- 成本高效:使用按需计费的无服务器架构。 |
| 122 | + |
| 123 | +结论 |
| 124 | +Azure Functions 是构建简洁、高效、可扩展的 MCP 远程服务器的理想工具,适用于各种边缘计算或远程设备通信场景。 |
| 125 | + |
| 126 | +5、[使用向量化加速 C# 性能](https://btburnett.com/csharp/2024/12/09/using-vectorization-in-csharp-to-boost-performance) |
| 127 | + |
| 128 | +SIMD(单指令多数据)是一种并行处理技术,允许 CPU 在一个指令周期内处理多个数据项。在 .NET 中,SIMD 通过 Vector<T> 类型实现,例如 `Vector128<T>`、`Vector256<T>` 等,这些类型根据 CPU 的支持情况提供不同大小的向量操作。 |
| 129 | +例如,使用传统方法对整数数组求和: |
| 130 | + |
| 131 | +```csharp |
| 132 | +public int Sum(ReadOnlySpan<int> values) |
| 133 | +{ |
| 134 | + int accumulator = 0; |
| 135 | + foreach (var i in values) |
| 136 | + { |
| 137 | + accumulator += i; |
| 138 | + } |
| 139 | + return accumulator; |
| 140 | +} |
| 141 | +``` |
| 142 | +在一次即兴的 SIMD 教学中,作者查看了 .NET 源代码中 Sum 方法的实现,发现它并未使用向量化: |
| 143 | + |
| 144 | +```csharp |
| 145 | +private static TResult Sum<T, TResult>(ReadOnlySpan<T> span) |
| 146 | + where T : struct, INumber<T> |
| 147 | + where TResult : struct, INumber<TResult> |
| 148 | +{ |
| 149 | + TResult sum = TResult.Zero; |
| 150 | + foreach (T value in span) |
| 151 | + { |
| 152 | + checked { sum += TResult.CreateChecked(value); } |
| 153 | + } |
| 154 | + return sum; |
| 155 | +} |
| 156 | +``` |
| 157 | +这让作者认为有机会通过向量化来优化该方法。作者编写了使用 `Vector<T>` 的向量化版本,并在性能测试中取得了数量级的提升。然而,他意识到原始方法使用了 checked 关键字来进行溢出检查,而 SIMD 加法操作默认忽略溢出,因此新方法在功能上并不等价。 |
| 158 | +为了在向量化的同时保留溢出检测,作者研究了二进制补码的原理,并使用位操作来检测溢出: |
| 159 | + |
| 160 | +```csharp |
| 161 | +int sum = x + y; |
| 162 | +bool isOverflow = (sum ^ x) & (sum ^ y) & int.MinValue != 0; |
| 163 | +``` |
| 164 | + |
| 165 | +作者的优化被合并到了 .NET 8 中,并在 Stephen Toub 的年度性能改进博客中提及。对于 1024 个 32 位整数的求和操作,性能从 .NET 7 的 347.28 纳秒提升到了 .NET 8 的 78.26 纳秒,提升了约 77%。 |
| 166 | + |
| 167 | +6、[构建 Web API 常见的 5 类错误](https://stefandjokic.tech/posts/building-apis-top-5-mistakes) |
| 168 | + |
| 169 | +构建 Web API 看似简单,实则复杂。本文总结了开发者在构建 API 时常犯的五个错误,并提供了相应的解决方案,旨在帮助开发者构建更健壮、可维护的 API。 |
| 170 | + |
| 171 | +错误一:不校验输入 |
| 172 | +开发者总是信任用户的输入,这样会导致程序崩溃或者安全漏洞 |
| 173 | +解决办法: |
| 174 | + |
| 175 | +- null 或者空字段校验 |
| 176 | +- 格式认证 |
| 177 | +- 业务逻辑认证 |
| 178 | + |
| 179 | +错误二:忽视版本控制 |
| 180 | +许多开发者在设计 API 时未考虑版本控制,导致后续更新难以兼容旧客户端。 |
| 181 | +解决方案: |
| 182 | + |
| 183 | +- 在 URL 或请求头中明确指定 API 版本,例如 /api/v1/。 |
| 184 | + |
| 185 | + |
| 186 | +错误三:错误的状态码使用 |
| 187 | +返回不准确的 HTTP 状态码会导致客户端误解响应结果。 |
| 188 | +解决方案: |
| 189 | +正确使用 2xx、4xx、5xx 等状态码,例如: |
| 190 | + |
| 191 | +- 200 OK:请求成功。 |
| 192 | +- 400 Bad Request:请求参数错误。 |
| 193 | +- 401 Unauthorized:未授权访问。 |
| 194 | +- 404 Not Found:资源不存在。 |
| 195 | +- 500 Internal Server Error:服务器内部错误。 |
| 196 | +确保错误响应中包含详细的错误信息,便于客户端调试。 |
| 197 | + |
| 198 | +错误四:复杂的返回类型 |
| 199 | +将数据库中的实体对象返回给请求,会导致数据库结构泄露,暴露敏感字段,导致返回请求的 body 过大 |
| 200 | +解决办法 |
| 201 | + |
| 202 | +- 返回数据库对象的的子集和必要字段 |
| 203 | + |
| 204 | +错误五:没有中心化的错误处理方式 |
| 205 | +每个 API 处理逻辑自己定义了错误逻辑,导致非常混乱。 |
| 206 | +解决方案: |
| 207 | + |
| 208 | +- 使用全局错误处理中间件 |
| 209 | + |
| 210 | +## 视频推荐 |
| 211 | + |
| 212 | +1、[Null 对象的最新语法](https://www.youtube.com/watch?v=HMSfIkYI5ls&ab_channel=NickChapsas) |
| 213 | + |
| 214 | +C# 的 nullable reference 类型有了新的语法,之前 `T?` 类型只能获取值的方式,而不能进行赋值,举例而言 |
| 215 | + |
| 216 | +```csharp |
| 217 | +class Student |
| 218 | +{ |
| 219 | + public string Name { get; set; } |
| 220 | + public int Age { get; set; } |
| 221 | +} |
| 222 | +Student? student = null; |
| 223 | +Console.WriteLine(student?.Name) |
| 224 | +``` |
| 225 | + |
| 226 | +但是如果我们想要更新 `student` 对象的时候,需要通过这种方式进行判断 |
| 227 | + |
| 228 | +```csharp |
| 229 | +if (student != null) |
| 230 | +{ |
| 231 | + student.Age = age; |
| 232 | +} |
| 233 | +``` |
| 234 | + |
| 235 | +现在新的语法 (preview 阶段) 的可以省略 `null` 判断 |
| 236 | + |
| 237 | +```csharp |
| 238 | +student?.Age = age; |
| 239 | +``` |
| 240 | + |
| 241 | +2、[如何不用dockerfile容器化一个.NET 应用程序](https://www.youtube.com/watch?v=PtGTU7thBuY) |
| 242 | + |
| 243 | +🎯 视频概述 |
| 244 | +本视频介绍了如何在不编写 Dockerfile 的情况下,将 .NET 应用容器化。Julio Casal 通过演示,展示了使用 .NET CLI 工具和内置功能,简化容器化流程,适用于希望快速部署 .NET 应用的开发者。 |
| 245 | + |
| 246 | +🧰 主要内容 |
| 247 | + |
| 248 | +1. 使用 .NET CLI 进行容器化 |
| 249 | +介绍了如何使用 dotnet publish 命令与相关参数,生成容器镜像,无需手动编写 Dockerfile。 |
| 250 | +2. 配置容器参数 |
| 251 | +演示了如何通过项目文件(.csproj)或命令行参数,设置容器的基础镜像、端口等配置。 |
| 252 | +3. 多平台支持 |
| 253 | +讨论了如何构建支持多平台的容器镜像,确保应用在不同环境中的兼容性。 |
| 254 | +4. 部署与运行 |
| 255 | +展示了如何将生成的容器镜像部署到本地或云端环境,并运行 .NET 应用 |
| 256 | + |
| 257 | +✅ 优点 |
| 258 | + |
| 259 | +- 简化了容器化流程,降低了入门门槛。 |
| 260 | +- 减少了对 Dockerfile 的依赖,适合快速开发和测试。 |
| 261 | +- 利用 .NET CLI 工具,提升了开发效率。 |
| 262 | + |
| 263 | +⚠️ 注意事项 |
| 264 | + |
| 265 | +- 对于需要复杂配置的生产环境,仍建议使用自定义的 Dockerfile。 |
| 266 | +- 了解生成的容器镜像的结构和配置,有助于更好地调试和优化。 |
| 267 | + |
| 268 | +3、[param 参数支持 Span](https://www.youtube.com/watch?v=xqk_ZabcM1M&ab_channel=NickChapsas) |
| 269 | + |
| 270 | +C# 支持 `param` `参数修饰符,用来表示可变长度参数,这样在调用方法的时候,可以无需构造数组,直接挨个传入对象即可。但是我们知道这个一个语法糖,编译器会在方法调用之前构造一个数组,然后将数组参数传入。 |
| 271 | +这样就会导致显而易见的问题,就是出现了堆内存分配的问题,如果对于性能敏感的路径,可以选择使用 `Span<T>` 而不是 `T[]` 作为参数类型,可以避免内存分配 |
| 272 | + |
| 273 | +```csharp |
| 274 | +[MemoryDiagnoser] |
| 275 | +public class ParamBenchmarks |
| 276 | +{ |
| 277 | + private static void UseParamArray(params string[] paramArray) |
| 278 | + { |
| 279 | + foreach (var item in paramArray) |
| 280 | + { |
| 281 | + } |
| 282 | + } |
| 283 | + |
| 284 | + private static void UseParamsSapn(params Span<string> paramArray) |
| 285 | + { |
| 286 | + foreach (var item in paramArray) |
| 287 | + { |
| 288 | + } |
| 289 | + } |
| 290 | + |
| 291 | + [Benchmark] |
| 292 | + public void TestParamArray() |
| 293 | + { |
| 294 | + UseParamArray("Hello", "World"); |
| 295 | + } |
| 296 | + |
| 297 | + [Benchmark] |
| 298 | + public void TestParamsSpan() |
| 299 | + { |
| 300 | + UseParamsSapn("Hello", "World"); |
| 301 | + } |
| 302 | +} |
| 303 | +``` |
| 304 | + |
| 305 | +结果如下 |
| 306 | + |
| 307 | +| Method | Mean | Error | StdDev | Median | Gen0 | Allocated | |
| 308 | +|--------------- |---------:|----------:|----------:|---------:|-------:|----------:| |
| 309 | +| TestParamArray | 3.315 ns | 0.0928 ns | 0.2736 ns | 3.188 ns | 0.0048 | 40 B | |
| 310 | +| TestParamsSpan | 1.360 ns | 0.0072 ns | 0.0060 ns | 1.357 ns | - | - | |
| 311 | + |
| 312 | +4、[为项目选择最佳.NET UI框架](https://www.youtube.com/watch?v=jIVzKKi0414) |
| 313 | + |
| 314 | + |
| 315 | + |
| 316 | +该视频由 .NET Foundation 发布,主题为“为你的项目选择最佳的 .NET UI 框架”。视频深入探讨了多种 .NET 用户界面框架的特点、适用场景和选择建议,帮助开发者根据项目需求做出明智决策。 |
| 317 | +视频内容概览: |
| 318 | + |
| 319 | +1. 主要 .NET UI 框架介绍: |
| 320 | +- Windows Forms:适用于传统桌面应用,开发简单,适合快速原型设计。 |
| 321 | +- WPF(Windows Presentation Foundation):支持丰富的用户界面和数据绑定,适合需要复杂界面的桌面应用。 |
| 322 | +- UWP(Universal Windows Platform):面向 Windows 10 及以上平台,支持多设备协同。 |
| 323 | +- MAUI(.NET Multi-platform App UI):.NET 6 推出的跨平台框架,支持 iOS、Android、Windows 和 macOS。 |
| 324 | +- Blazor:基于 Web 的 UI 框架,支持 WebAssembly 和服务器端渲染,适合构建现代 Web 应用。 |
| 325 | +2. 框架对比与选择建议: |
| 326 | +- 平台支持:根据目标平台选择合适的框架,例如 MAUI 适合跨平台需求,WPF 适合 Windows 桌面应用。 |
| 327 | +- 开发体验:考虑开发工具、社区支持和学习曲线,选择最适合团队的框架。 |
| 328 | +- 性能与功能:评估框架的性能表现和提供的功能特性,确保满足项目需求。 |
| 329 | +3. 实际案例分析: |
| 330 | +视频中可能包含一些实际项目的案例,展示不同框架在实际应用中的表现和优势。 |
| 331 | +4. 未来发展趋势: |
| 332 | +探讨 .NET UI 框架的未来发展方向,如 MAUI 的持续演进和 Blazor 在 Web 开发中的应用前景。 |
| 333 | + |
| 334 | +## 开源项目 |
| 335 | + |
| 336 | +1、[csharpier](https://github.com/belav/csharpier) |
| 337 | + |
| 338 | + |
| 339 | + |
| 340 | +CSharpier 是一个为 C# 和 XML 设计的“有主见”(opinionated)代码格式化工具,旨在通过统一的格式规则提升代码一致性和可读性。它借鉴了 [Prettier](https://prettier.io/) 的理念,使用 Roslyn 解析代码,并根据自身规则重新打印代码,自动处理缩进、换行和空格等格式细节。 |
| 341 | + |
| 342 | +✨ 特性 |
| 343 | + |
| 344 | +- 统一格式:自动处理缩进、换行和空格,确保代码风格一致。 |
| 345 | +- 最小配置:仅提供少量配置选项(如缩进宽度、使用空格或制表符),避免团队在格式问题上的争论。 |
| 346 | +- 快速高效:性能优异,适用于大型项目。 |
| 347 | +- 广泛集成:支持 Visual Studio、VS Code 等主流 IDE,可设置为保存时自动格式化。 |
| 348 | +- CI/CD 支持:可作为预提交钩子或在持续集成流程中使用,确保提交代码符合格式规范。 |
| 349 | +- XML 支持:从 1.0.0 版本起,支持格式化 .csproj、.xml、.config 等 XML 文件。 |
0 commit comments