|
| 1 | +# .NET 每周分享第 35 期 |
| 2 | + |
| 3 | +## 卷首语 |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +从诞生的第一天起, `C#` 就被认为是 `Java` 的模仿者。从历史来看,我们不能否认这一个观点。现在,每个编程语言都借鉴了其他的编程语言,那么从 `.NET Core` 之后,`C#` 又从 `Java` 中借鉴了什么呢?主要包含三个 |
| 8 | + |
| 9 | +1. Record 类型 |
| 10 | + |
| 11 | +Record 类型是用来表示纯数据的结构,`C#` 编译器能够将它转换成一个 `class` 类型,并且重写了 `Equals` 方法。 |
| 12 | + |
| 13 | +2. 字面字符串 |
| 14 | + |
| 15 | +在 `C#` 中,我们使用双引号 `"` 来表示一个字符串,但是对于一些复杂的字符串,比如换行,特殊字符,我们需要通过转义的方式来表示。这样带来了很多使用的不便。`C#` 引入的字面字符串的方式 |
| 16 | + |
| 17 | +```csharp |
| 18 | +var text = """ |
| 19 | + { |
| 20 | + "name": "fung kao" |
| 21 | + } |
| 22 | +"""; |
| 23 | +``` |
| 24 | + |
| 25 | +3. 枚举类型的箭头表达 |
| 26 | + |
| 27 | +对于枚举类型,我们一般采用 `swtich` 的方式,比如 |
| 28 | + |
| 29 | +```csharp |
| 30 | +enum Role |
| 31 | +{ |
| 32 | + User, |
| 33 | + Admin |
| 34 | +} |
| 35 | + |
| 36 | +var role = Role.User; |
| 37 | + |
| 38 | +switch(role) |
| 39 | +{ |
| 40 | + case Role.User: |
| 41 | + Console.WriteLine("User"); |
| 42 | + break; |
| 43 | + case Role.Admin: |
| 44 | + Console.WriteLine("Admin"); |
| 45 | + break; |
| 46 | + default: |
| 47 | + Console.WriteLine("Unknown"); |
| 48 | + break; |
| 49 | +} |
| 50 | +``` |
| 51 | + |
| 52 | +这是这个常见的用法,C# 引入了模式匹配的方式,我们可以这样使用 |
| 53 | + |
| 54 | +```csharp |
| 55 | +var text = role swtich |
| 56 | +{ |
| 57 | +Role.User => "User", |
| 58 | +Role.Admin => "Admin" |
| 59 | +_ => "unknown" |
| 60 | +} |
| 61 | +``` |
| 62 | + |
| 63 | +## 行业资讯 |
| 64 | + |
| 65 | +1、[.NET 虚拟大会](https://devblogs.microsoft.com/dotnet/lets-learn-dotnet-anywhere-in-the-world/) |
| 66 | +
|
| 67 | + |
| 68 | +
|
| 69 | +通常而言,英语是 `.NET` 社区常用的沟通语言。但是最近 `.NET` 社区举办了全球的虚拟大会,而且在不同的时区和不同语言,值得大家参加。 |
| 70 | + |
| 71 | +## 文章推荐 |
| 72 | + |
| 73 | +1、[.NET 的 Timer](https://www.meziantou.net/too-many-timers-in-dotnet.htm) |
| 74 | +
|
| 75 | + |
| 76 | +
|
| 77 | +C# 中包含了很多 `Timer` 类,一般都用来进行一些定时的操作。主要分为两大类: |
| 78 | + |
| 79 | +1. UI 定时类 |
| 80 | + |
| 81 | +- `System.Windows.Forms.Timer` |
| 82 | +- `System.Windows.Threading.DispatcherTimer` |
| 83 | + 它们都是 UI 线程执行回调,所以它们能够更新 UI 上的元素,而且每次只执行一次,因此不用担心线程安全的问题 |
| 84 | + |
| 85 | +2. 通用定时器 |
| 86 | + |
| 87 | +- `System.Threading.Timer` |
| 88 | +- `System.Threading.PeriodicTimer` |
| 89 | +- `System.Timers.Timer` |
| 90 | + |
| 91 | +其中 `System.Threading.Timer` 是最基础的定时器,它将回调委托给线程池执行。但是如果回调执行的时间超过定时器的间隔,那么会发生同时执行多次的情况。 |
| 92 | + |
| 93 | +```csharp |
| 94 | +var timer = new System.Threading.Timer( |
| 95 | + callback: state => Console.WriteLine("tick"), // callback can be executed in parallel |
| 96 | + // if the previous one is not completed |
| 97 | + // before the next tick |
| 98 | + state: null, // Can be used to pass data to the callback (to avoid using a closure) |
| 99 | + dueTime: TimeSpan.Zero, // Start the timer immediately |
| 100 | + period: TimeSpan.FromSeconds(1)); // Tick every second |
| 101 | +
|
| 102 | +// Pause the timer |
| 103 | +timer.Change(dueTime: Timeout.Infinite, period: Timeout.Infinite); |
| 104 | +``` |
| 105 | + |
| 106 | +`System.Timer.Timer` 是对 `System.Threading.Timer` 的封装,而且暴露了其他几个方法,比如 `AutoReset`, `Enabled`,`SynchronizingObject` 等等,而且它还支持多个回调的调用。 |
| 107 | + |
| 108 | +```csharp |
| 109 | +var timer = new System.Timers.Timer(TimeSpan.FromSeconds(1)); |
| 110 | + |
| 111 | +// Support multiple handlers |
| 112 | +timer.Elapsed += (sender, e) => Console.WriteLine("Handler 1"); |
| 113 | +timer.Elapsed += (sender, e) => Console.WriteLine("Handler 2"); |
| 114 | + |
| 115 | +// Support customizing the way the callback is executed (on the ThreadPool if not set) |
| 116 | +timerComponent.SynchronizingObject = ...; |
| 117 | + |
| 118 | +// Stop the timer after the first tick |
| 119 | +timerComponent.AutoReset = false; |
| 120 | + |
| 121 | +// Start the timer |
| 122 | +timer.Start(); |
| 123 | +``` |
| 124 | + |
| 125 | +`System.Threading.PeriodicTimer` 是最近加入的类型,它的主要功能是允许定时器在循环中使用,而且执行异步。 |
| 126 | + |
| 127 | +```csharp |
| 128 | +using var cts = new CancellationTokenSource(); |
| 129 | +using var periodicTimer = new PeriodicTimer(TimeSpan.FromSeconds(1)); |
| 130 | + |
| 131 | +// Simple usage, no concurrent callbacks, supports async _handlers_ |
| 132 | +while (await periodicTimer.WaitForNextTickAsync(cts.Token)) |
| 133 | +{ |
| 134 | + Console.WriteLine("Tick"); |
| 135 | + await AsyncOperation(); |
| 136 | +} |
| 137 | +``` |
| 138 | + |
| 139 | +通过 `while` 循环判断,避免了回调的并行执行。 |
| 140 | + |
| 141 | +2、[.NET Standard 介绍](https://andrewlock.net/understanding-the-dotnet-ecosystem-the-introduction-of-dotnet-standard/) |
| 142 | +
|
| 143 | + |
| 144 | +
|
| 145 | +`.NET Standard` 作为一个短暂的技术名词,在 `.NET` 历史中存在过一段时间。主要目的是解决 `.NET` 各个平台的之间代码的复用性,比如 `.NET Framework`, `Mono` 或者 `.NET Core`。它是一组 `API` 定义的集合,而各个平台包含这些 `API` 的实现。 |
| 146 | + |
| 147 | +3、[String 和对象互转换](https://csharp.christiannagel.com/2023/04/14/iparsable/) |
| 148 | +
|
| 149 | + |
| 150 | +
|
| 151 | +将一个对象实例和字符串之间相互转换是很常见的要求,在 `C#` 中定义了接口来进行这些转换。 |
| 152 | + |
| 153 | +1. 对象实例转换字符串 |
| 154 | + |
| 155 | +- IFormattable |
| 156 | +- ISpanFormattable |
| 157 | + |
| 158 | +只要对象实现了其中的接口,那么可以将对象实例按照要求转换成字符串类型 |
| 159 | + |
| 160 | +2. 字符串转换成对象 |
| 161 | + |
| 162 | +- IParsable |
| 163 | +- ISpanParsable |
| 164 | + |
| 165 | +`.NET 7` 为接口类型提供了静态方法,所以在 `.NET 7` 之前,将一个字符串转换成对象的话,通常是这么处理的 |
| 166 | + |
| 167 | +```csharp |
| 168 | +class Person { |
| 169 | + public string FirstName { get; } |
| 170 | + public string FullName { get; } |
| 171 | + public string Country { get; } |
| 172 | + |
| 173 | + public Person(string firstName, string fullName, string country) { |
| 174 | + FirstName = firstName; |
| 175 | + FullName = fullName; |
| 176 | + Country = country; |
| 177 | + } |
| 178 | +} |
| 179 | + |
| 180 | +static class ExtensionMethods { |
| 181 | + internal static Person Parse(this string s) { |
| 182 | + string[] strings = s.Split(new[] { ',', ';' }); |
| 183 | + if(strings.Length != 3) { throw new OverflowException("Expect: FirstName,LastName,Country"); } |
| 184 | + return new Person(strings[0], strings[1], strings[2]); |
| 185 | + } |
| 186 | +} |
| 187 | +``` |
| 188 | + |
| 189 | +通常我们需要编写 `string` 类型的扩展方法,那么在 `.NET 7` 中,我们可以通过集成 `IParsable` 接口完成更优雅的实现方式。 |
| 190 | + |
| 191 | +```csharp |
| 192 | +sealed class Person : IParsable<Person> { |
| 193 | + public string FirstName { get; } |
| 194 | + public string FullName { get; } |
| 195 | + public string Country { get; } |
| 196 | + |
| 197 | + // Private constructor used from the Parse() method below |
| 198 | + private Person(string firstName, string fullName, string country) { |
| 199 | + FirstName = firstName; |
| 200 | + FullName = fullName; |
| 201 | + Country = country; |
| 202 | + } |
| 203 | + |
| 204 | + // IParsable<Person> implementation |
| 205 | + public static Person Parse(string s, IFormatProvider? provider) { |
| 206 | + string[] strings = s.Split(new[] { ',', ';' }); |
| 207 | + if(strings.Length != 3) { throw new OverflowException("Expect: FirstName,LastName,Country"); } |
| 208 | + return new Person(strings[0], strings[1], strings[2]); |
| 209 | + } |
| 210 | + |
| 211 | + public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Person result) { |
| 212 | + result = null; |
| 213 | + if (s == null) { return false; } |
| 214 | + try { |
| 215 | + result = Parse(s, provider); |
| 216 | + return true; |
| 217 | + } catch { return false; } |
| 218 | + } |
| 219 | +} |
| 220 | + |
| 221 | +Person person = "Bill,Gates,US".Parse<Person>(); |
| 222 | +``` |
| 223 | + |
| 224 | +4、[Azure OpenAI 使用](https://devblogs.microsoft.com/dotnet/getting-started-azure-openai-dotnet/) |
| 225 | +
|
| 226 | + |
| 227 | +
|
| 228 | +作为一名 `.NET` 开发者,该如何上手 `Azure OpenAI` 呢?这篇官方博客给出了建议。 |
| 229 | + |
| 230 | +5、ASP.NET Core 中间件处理流程 |
| 231 | + |
| 232 | + |
| 233 | +
|
| 234 | +6、[WASM 介绍](https://speakerdeck.com/christianweyer/wasm-wasi-wtf-webassembly-101-for-net-developers) |
| 235 | +
|
| 236 | + |
| 237 | +
|
| 238 | +WASM 是新的技术,`.NET` 也不能落后,这个幻灯片介绍了 `WASM` 和 `C#` 入门知识。 |
| 239 | + |
| 240 | +7、[Mutation Test](https://blog.jetbrains.com/dotnet/2023/05/05/stefan-polz-how-to-test-csharp-unit-tests-with-mutation-testing-webinar-recording/) |
| 241 | +
|
| 242 | +Mutation Test 是用来检测单元测试的质量,这个视频详细介绍这个概念。 |
| 243 | + |
| 244 | +## 开源项目 |
| 245 | + |
| 246 | +1、[Powershell 中的 chatgpt](https://dfinke.github.io/powershellai,%20powershell,%20chatgpt/2023/04/04/PowerShellAI-ChatGPT-Conversation-Mode.html) |
| 247 | +
|
| 248 | + |
| 249 | +
|
| 250 | +`ChatGPT` 火了,各种针对 `OpenAI` 的 API 开发的 AI 应用程序也数不胜数。 `PowerShellAI` 是一个开源的 PowerShell 库,使用它我们可以在 `Powershell` 中使用我们的人工智能助手。 |
| 251 | + |
| 252 | +2、.NET 知名开源库 |
| 253 | + |
| 254 | + |
0 commit comments