|
| 1 | +# .NET 每周分享第 49 期 |
| 2 | + |
| 3 | +## 卷首语 |
| 4 | + |
| 5 | +C# 成为年度语言? 这里有一个有趣的不同的[观点](https://www.youtube.com/watch?v=5omutyj6N0Q&t=409s&ab_channel=NickChapsas)。 |
| 6 | + |
| 7 | +在过去的一段时间,`.NET` 社区最大的新闻是 `C#` 被 Tiobe 评选为 2023 年度编程语言。几乎所有的媒体都在欢呼雀跃,那么事实真的如此吗? |
| 8 | + |
| 9 | +1. Tiobe 评选的标准是增加量 |
| 10 | +2. 结果并不会改变立刻任何事情 |
| 11 | +3. Tiobe 的选择的可靠性不高 |
| 12 | + |
| 13 | +## 文章推荐 |
| 14 | + |
| 15 | +1、[StringValue 探秘](https://andrewlock.net/a-brief-look-at-stringvalues/) |
| 16 | + |
| 17 | +在 `Microsoft.Extensions.Primitives` 包中有一个类型是 `StringValues`, 它可以用在表示 `HTTP` Header 中,可以表达三种情况, |
| 18 | + |
| 19 | +1. 没有 Header |
| 20 | +2. 单个 Header |
| 21 | +3. 多个 Header |
| 22 | + |
| 23 | +那么在 `ASP.NET Core` 中,该如何表示这个对象呢? |
| 24 | + |
| 25 | +- 朴素表达方式 |
| 26 | + |
| 27 | +使用 `Dictionary<string, string[]>` 类型表达,因为上述三种情况都可以表示,但是这个存在两个问题: |
| 28 | + |
| 29 | +1. 大部分情况下,只有一个值,但是还是需要多个情况 |
| 30 | +2. 将单个值存储为一个数组,增加内存分配 |
| 31 | + |
| 32 | +- `NameValueCollection` |
| 33 | + |
| 34 | +在 `ASP.NET` 中使用 `NameValueCollection` 类型,但是它本质还是使用 `ArrayList` 存储内容, 所以还是不够优雅。 |
| 35 | + |
| 36 | +在 `StringValues` 使用一个 `object` 表示上述的三种情况,也是一个 `readonly struct` 类型。 |
| 37 | + |
| 38 | +2、[.NET 8 中 Random](https://henriquesd.medium.com/net-8-new-randomness-methods-f2422f55320f) |
| 39 | + |
| 40 | + |
| 41 | + |
| 42 | +.NET 8 在 `Random` 类中提供了一些新的方法,比如 |
| 43 | + |
| 44 | +1. GetItems |
| 45 | + |
| 46 | +当我们在一个集合中随机选择一定数量时候,可以使用 `GetItems`, 比如 `Random.Shard.GetItems`。 |
| 47 | + |
| 48 | +2. Shuffle |
| 49 | + |
| 50 | +如果对一个集合进行混洗,可以使用 `Shuffle`, 比如 `Random.Shard.Shuffle`。 |
| 51 | + |
| 52 | +3、[.NET 8 中并行启动 IHostedService](https://www.youtube.com/watch?v=n4tzpRB2lzc&ab_channel=MilanJovanovi%C4%87) |
| 53 | + |
| 54 | + |
| 55 | + |
| 56 | +在 `IHost` 中,在执行 `Run` 方法的时候,会获取所有的注册的 `IHostedService` 取出来,然后执行 `StartAsync` 方法。要注意的是这个方法是顺序执行的,在 `.NET 8` 中,可以设置 `HostOption` 的 `ServicesStartConcurrently` 和 `ServicesStopConcurrently` 方法让启动或者停止并行执行。 |
| 57 | + |
| 58 | +```csharp |
| 59 | +new HostBuilder() |
| 60 | + .ConfigureServices(service => |
| 61 | + { |
| 62 | + service.Configure<HostOptions>(options => |
| 63 | + { |
| 64 | + options.ServicesStartConcurrently = true; |
| 65 | + options.ServicesStopConcurrently = true; |
| 66 | + }); |
| 67 | + service.AddHostedService<FooService>(); |
| 68 | + service.AddHostedService<BarService>(); |
| 69 | + }) |
| 70 | + .Build() |
| 71 | + .Run(); |
| 72 | + |
| 73 | +internal class FooService : IHostedService |
| 74 | +{ |
| 75 | + public async Task StartAsync(CancellationToken cancellationToken) |
| 76 | + { |
| 77 | + Console.WriteLine("Start Foo"); |
| 78 | + await Task.Delay(2000); |
| 79 | + } |
| 80 | + |
| 81 | + public async Task StopAsync(CancellationToken cancellationToken) |
| 82 | + { |
| 83 | + Console.WriteLine("Stop Foo"); |
| 84 | + await Task.Delay(2000); |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +internal class BarService : IHostedService |
| 89 | +{ |
| 90 | + public async Task StartAsync(CancellationToken cancellationToken) |
| 91 | + { |
| 92 | + Console.WriteLine("Start Bar"); |
| 93 | + await Task.Delay(1000); |
| 94 | + } |
| 95 | + |
| 96 | + public async Task StopAsync(CancellationToken cancellationToken) |
| 97 | + { |
| 98 | + Console.WriteLine("Stop Bar"); |
| 99 | + await Task.Delay(1000); |
| 100 | + } |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +4、[查看 JIT 代码的三种方法](https://www.meziantou.net/how-to-get-assembly-code-generated-by-the-jit-for-a-csharp-method.htm) |
| 105 | + |
| 106 | +C# 支持查看 `JIT` 生成的机器代码,那么该如何查看它们呢?有三种方法 |
| 107 | + |
| 108 | +1. DOTNET_JitDisasm="Method" |
| 109 | + |
| 110 | +```csharp |
| 111 | +Foo.Bar(); |
| 112 | +class Foo |
| 113 | +{ |
| 114 | + public static void Bar() |
| 115 | + { |
| 116 | + Console.WriteLine("Hello World!"); |
| 117 | + } |
| 118 | +} |
| 119 | +``` |
| 120 | + |
| 121 | +如果设置 `DOTNET_JitDisasm="BAR"` ,那么在执行这个方法的时候,就能看到生成 `JIT` 代码 |
| 122 | + |
| 123 | +2. Disasmo |
| 124 | + |
| 125 | +`Disasmo` 是 Visual Studio 的插件,用来查看生成的 JIT 代码。 |
| 126 | + |
| 127 | +3. Sharplab |
| 128 | + |
| 129 | +[Sharplab](https://sharplab.io/) 是一个非常有用的 Web 站点,它可以对相关的代码生成相应的 JIT 代码方便查看。 |
| 130 | + |
| 131 | +5、[DTO 和 POCO 的区别](https://ardalis.com/dto-or-poco/#sq_hhjrkq9ir7) |
| 132 | + |
| 133 | + |
| 134 | + |
| 135 | +`DTO` 和 `POCO` 是在开发工程中经常遇到的概念,那么它们区别是怎样的呢? |
| 136 | + |
| 137 | +- DTO |
| 138 | + |
| 139 | +DTO 是 `Data Transfer Object` 的简写,它表明只是传递数据,而不包含逻辑和行为。在 C# 中就是只有只有属性,而不是有方法或者属性的注解。 |
| 140 | + |
| 141 | +- POCO |
| 142 | + |
| 143 | +POCO 是 `Plain Old CLR Object` 的简称,它表明这个类不依赖任何第三方的库或者框架。 |
| 144 | + |
| 145 | +6、[.NET 最快的 1BRC](https://hotforknowledge.com/2024/01/13/1brc-in-dotnet-among-fastest-on-linux-my-optimization-journey/#results-table) |
| 146 | + |
| 147 | +在今年开始的时候,`GitHub` 爆火了 ”十亿行挑战" 活动。从原先的 `Java` 语言到所有开发语言。在 `.NET` 范围内,目前最快记录是 `1.297` 秒,作者介绍了自己的优化之路。 |
| 148 | + |
| 149 | +## 开源项目 |
| 150 | + |
| 151 | +1、[NuGet Resolver](https://devblogs.microsoft.com/nuget/introducing-nugetsolver-a-powerful-tool-for-resolving-nuget-dependency-conflicts-in-visual-studio/) |
| 152 | + |
| 153 | +[image](https://github.com/DotNETWeekly-io/DotNetWeekly/assets/11272110/0edc9aba-4365-4ae2-939e-4438b8c5d66b) |
| 154 | + |
| 155 | +在一些大型 `.NET` 项目中,常常会遇到依赖冲突的问题,比如 |
| 156 | + |
| 157 | +- 无法解析依赖的一致性问题 |
| 158 | +- 依赖包不包含适合项目的资源包 |
| 159 | +- 依赖的包版本大于最终解析版本 |
| 160 | + |
| 161 | +等等问题,`Nuget` 团队和 MSR 团队合作,推出了 `NuGetSolver` 中 `Visual Studio` 插件,它可以解决上述的问题。但是目前有一些限制 |
| 162 | + |
| 163 | +1. 只支持 `nuget.org` 源 |
| 164 | +2. 不支持多个源 |
| 165 | +3. 不支持自动更新版本 |
| 166 | +4. 不支持 preview 版本 |
| 167 | +5. `package.config` 等老式版本不支持 |
| 168 | +6. 只支持编译依赖冲突 |
| 169 | + |
| 170 | +2、[Rx.NET](https://github.com/dotnet/reactive) |
| 171 | + |
| 172 | + |
| 173 | + |
| 174 | +`Rx.NET` 是一个开源的 `.NET` 事件库,可以帮助实现响应式编程,这样你的代码只需要响应对应的事件。 |
| 175 | + |
| 176 | +```csharp |
| 177 | +using System.Reactive.Linq; |
| 178 | + |
| 179 | +IObservable<long> ticks = Observable.Timer( |
| 180 | + dueTime: TimeSpan.Zero, |
| 181 | + period: TimeSpan.FromSeconds(1)); |
| 182 | + |
| 183 | +ticks.Subscribe(tick => Console.WriteLine($"Tick {tick}")); |
| 184 | + |
| 185 | +Console.ReadKey(); |
| 186 | +``` |
| 187 | + |
| 188 | +3、[MSTest Runner](https://devblogs.microsoft.com/dotnet/introducing-ms-test-runner/) |
| 189 | + |
| 190 | + |
| 191 | + |
| 192 | +微软推出了 `MSTest Runner` 这个测试框架,它更加轻量级,测试更快并且提供更加便携的测试机制。主要修改两个地方 |
| 193 | + |
| 194 | +```xml |
| 195 | +<Project Sdk="Microsoft.NET.Sdk"> |
| 196 | + |
| 197 | + <PropertyGroup> |
| 198 | + <!-- Enable the MSTest runner, this is an opt-in feature --> |
| 199 | + <EnableMSTestRunner>true</EnableMSTestRunner> |
| 200 | + <!-- We need to produce an executable and not a DLL --> |
| 201 | + <OutputType>Exe</OutputType> |
| 202 | + </PropertyGroup> |
| 203 | + |
| 204 | + <ItemGroup> |
| 205 | + <PackageReference Include="MSTest" Version="3.2.0" /> |
| 206 | + </ItemGroup> |
| 207 | + |
| 208 | +</Project> |
| 209 | +``` |
| 210 | + |
| 211 | +这样只需要执行生成的 `exe` 就可以运行单元测试。 |
0 commit comments