|
| 1 | +# .NET 每周分享第 47 期 |
| 2 | + |
| 3 | +## 卷首语 |
| 4 | + |
| 5 | +2023 年最后一期,感谢大家一直以来的支持,我们会在 2024 年继续为大家带来更多的内容。 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +## 行业资讯 |
| 10 | + |
| 11 | +1、[.NET 8 Hack 活动获奖](https://devblogs.microsoft.com/dotnet/great-dotnet-8-hack-winners/) |
| 12 | + |
| 13 | + |
| 14 | + |
| 15 | +前几个月 `.NET` 社区发起了 "黑客"活动,最近这个活动的获奖情况揭晓: |
| 16 | + |
| 17 | +- 最佳:NASA TechPort Headlines |
| 18 | +- AI 最佳:Betakads |
| 19 | +- 云原生最佳:AI Counselor |
| 20 | + |
| 21 | + |
| 22 | +2、[.NET Conf 2023 回顾](https://devblogs.microsoft.com/dotnet/dotnet-conf-2023-recap-videos-slides-demos-and-more/) |
| 23 | + |
| 24 | + |
| 25 | + |
| 26 | +2023 年的 `.NET Conf` 已经结束,这个文章给出活动的回顾,主要有: |
| 27 | + |
| 28 | +- 全局视频列表 |
| 29 | +- 幻灯片和 Demo 代码 |
| 30 | +- eShop 应用程序 |
| 31 | +- 客户故事 |
| 32 | +- 本地 `.NET` 活动 |
| 33 | + |
| 34 | +## 文章推荐 |
| 35 | + |
| 36 | +1、[HttpClient 定制化日志](https://josef.codes/customize-the-httpclient-logging-dotnet-core/) |
| 37 | + |
| 38 | + |
| 39 | + |
| 40 | +`HttpClient` 默认的日志格式是这样的 |
| 41 | + |
| 42 | +```csharp |
| 43 | +info: System.Net.Http.HttpClient.my-client.LogicalHandler[100] |
| 44 | + Start processing HTTP request GET https://www.google.com |
| 45 | +``` |
| 46 | + |
| 47 | +如果想要定制化,可以实现 `IHttpClientLogger` 接口 |
| 48 | + |
| 49 | +```csharp |
| 50 | +public class HttpLogger : IHttpClientLogger |
| 51 | +{ |
| 52 | + private readonly ILogger<HttpLogger> _logger; |
| 53 | + |
| 54 | + public HttpLogger(ILogger<HttpLogger> logger) |
| 55 | + { |
| 56 | + _logger = logger |
| 57 | + } |
| 58 | + |
| 59 | + public void LogRequestFailed(object? context, HttpRequestMessage request, HttpResponseMessage? response, Exception exception, TimeSpan elapsed) |
| 60 | + { |
| 61 | + } |
| 62 | + |
| 63 | + public object? LogRequestStart(HttpRequestMessage request) |
| 64 | + { |
| 65 | + } |
| 66 | + |
| 67 | + public void LogRequestStop(object? context, HttpRequestMessage request, HttpResponseMessage response, TimeSpan elapsed) |
| 68 | + { |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +``` |
| 73 | + |
| 74 | +这样可以将它注册到容器中 |
| 75 | + |
| 76 | +```csharp |
| 77 | +service.AddSingleton<IHttpClientLogger, HttpLogger>(); |
| 78 | +service.AddHttpClient("my-client", client => |
| 79 | +{ |
| 80 | + client.BaseAddress = new Uri("https://www.google.com"); |
| 81 | +}).RemoveAllLoggers().AddLogger<IHttpClientLogger>(true); |
| 82 | +``` |
| 83 | +为了移除默认的日志格式,需要调用 `RemoveAllLoggers()` 方法。 |
| 84 | + |
| 85 | +2、[All 和 TrueForAll](https://www.youtube.com/watch?v=cpL-fuiEfwU&ab_channel=NickChapsas) |
| 86 | + |
| 87 | + |
| 88 | + |
| 89 | +在 `List` 类型中有一个 `TrueForAll` 和 `IEnumerable` 中 `All` 方法都是完成同一件事,但是它们在性能上有显著的区别。 |
| 90 | +```csharp |
| 91 | +public class Benchmarks |
| 92 | +{ |
| 93 | + private List<int> _list; |
| 94 | + |
| 95 | + [GlobalSetup] |
| 96 | + public void Setup() |
| 97 | + { |
| 98 | + Random rnd = new Random(42); |
| 99 | + _list = Enumerable.Range(0, 1000).Select(i => rnd.Next()).ToList(); |
| 100 | + } |
| 101 | + |
| 102 | + |
| 103 | + [Benchmark(Baseline = true)] |
| 104 | + public bool All() |
| 105 | + { |
| 106 | + return _list.All(x => x < 1000); |
| 107 | + } |
| 108 | + |
| 109 | + [Benchmark] |
| 110 | + public bool TrueForAll() |
| 111 | + { |
| 112 | + return _list.TrueForAll(x => x < 1000); |
| 113 | + } |
| 114 | +} |
| 115 | +``` |
| 116 | +它们在性能和内存使用上,`TrueForAll` 有着显著的优势。它们区别这在于 |
| 117 | +```csharp |
| 118 | +public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) |
| 119 | +{ |
| 120 | + foreach (TSource element in source) |
| 121 | + { |
| 122 | + if (!predicate(element)) |
| 123 | + { |
| 124 | + return false; |
| 125 | + } |
| 126 | + } |
| 127 | + return true; |
| 128 | +} |
| 129 | + |
| 130 | +public bool TrueForAll(Predicate<T> match) |
| 131 | +{ |
| 132 | + for (int i = 0; i < _size; i++) |
| 133 | + { |
| 134 | + if (!match(_items[i])) |
| 135 | + { |
| 136 | + return false; |
| 137 | + } |
| 138 | + } |
| 139 | + return true; |
| 140 | +} |
| 141 | +``` |
| 142 | + |
| 143 | +答案显而易见,因为 `All` 方法使用 `Foreach` 方法,因为它存在装箱和拆箱的操作。 |
| 144 | + |
| 145 | + |
| 146 | +3、[OR 操作符](https://www.youtube.com/watch?v=wPmh_ZtFQ5k&ab_channel=MilanJovanovi%C4%87) |
| 147 | + |
| 148 | +C# 中 `or` 操作符可以简化 `||` 操作。 |
| 149 | + |
| 150 | +```csharp |
| 151 | +if (student.Grade == Grade.Excellent || |
| 152 | +(student.Grade == Grade.Good) |
| 153 | +{ |
| 154 | + Console.WriteLine("Well done"); |
| 155 | +} |
| 156 | +``` |
| 157 | +那么 `OR` 操作符可以简化成一行代码 |
| 158 | + |
| 159 | +```csharp |
| 160 | +if (student.Grade is Grade.Excellent or Grade.Good) |
| 161 | +{ |
| 162 | + Console.WriteLine("Well done"); |
| 163 | +} |
| 164 | +``` |
| 165 | + |
| 166 | +4、[为什么我现在不用 Aspire](https://event-driven.io/en/nay_to_aspire/) |
| 167 | +
|
| 168 | + |
| 169 | +
|
| 170 | +作者在使用了 `Aspire` 之后,给出了负面的评价,观点如下 |
| 171 | + |
| 172 | +- 启动太复杂 |
| 173 | +- 所有项目都在同一个解决方案下 |
| 174 | +- 在 Linux 下,本地环境非常难管理 |
| 175 | + |
| 176 | +5、[c# 中的不可变类型](https://blog.stephencleary.com/2023/12/the-joy-of-immutable-update-patterns.html) |
| 177 | +
|
| 178 | + |
| 179 | +
|
| 180 | +`Immutable` 类型有很多好处,在 `C#` 中存在两种不可变类型 |
| 181 | + |
| 182 | +- 基础类型,比如 int, double 等 |
| 183 | +- 值类型,比如 struct |
| 184 | + |
| 185 | +除此之外,还有 `System.Collections.Immutable` 命名空间下类型。如果要对不可变类型做修改,通常是需要创建也给新的对象,比如在 switch 和 `with` 语句是现代 `C#` 代码创新不可变类型的方法。 |
| 186 | + |
| 187 | +6、[Visual Studio Git 集成汇总](https://devblogs.microsoft.com/visualstudio/a-year-of-making-you-more-productive-using-git-in-visual-studio/) |
| 188 | +
|
| 189 | + |
| 190 | +
|
| 191 | +过去一年 `Visual Studio` 在 `Git` 上面的提升汇总。 |
| 192 | + |
| 193 | +## 开源项目 |
| 194 | + |
| 195 | +1、[GitHub Action Visual Studio 插件](https://marketplace.visualstudio.com/items?itemName=TimHeuer.GitHubActionsVS) |
| 196 | +
|
| 197 | + |
| 198 | +
|
| 199 | +Visual Studio 包含了 `GitHub Action` 的插件,它可以帮助我们浏览,管理和运行 `GitHub Action`. |
| 200 | + |
| 201 | +2、[Cocona](https://github.com/mayuki/Cocona) |
| 202 | +
|
| 203 | + |
| 204 | +
|
| 205 | +`Cocona` 库可以帮助我们非常简单写出应用台应用程序。 |
| 206 | + |
| 207 | +```csharp |
| 208 | +using Cocona; |
| 209 | + |
| 210 | +CoconaApp.Run((string name) => |
| 211 | +{ |
| 212 | + Console.WriteLine($"hello {name}"); |
| 213 | +}); |
| 214 | +``` |
| 215 | +这样运行 `dotnet run -- --name .netwekly` 就可以输出 `hello .netweekly` 。 |
0 commit comments