|
| 1 | +# .NET 每周分享第 44 期 |
| 2 | + |
| 3 | +## 卷首语 |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +微软推出了 `.NET 8` Hack [项目](https://devblogs.microsoft.com/dotnet/join-us-for-the-great-dotnet-8-hack/),在 11 月 20 号到 12 月 4 号期间,使用 `.NET 8`开发云原生,AI 或者 MAUI 的项目都可以获取将奖金。 |
| 8 | + |
| 9 | +## 行业资讯 |
| 10 | + |
| 11 | +1、[F# 编程项目](https://d3s.mff.cuni.cz/teaching/nprg077/) |
| 12 | + |
| 13 | + |
| 14 | + |
| 15 | +这里包含了四门课程,主要是是通过 `F#` 编写自己的编程系统。 |
| 16 | + |
| 17 | +## 文章推荐 |
| 18 | + |
| 19 | +1、[Logger Message Generator](https://learn.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator) |
| 20 | + |
| 21 | + |
| 22 | + |
| 23 | +Log 的方法的的签名是这样的 |
| 24 | + |
| 25 | +```csharp |
| 26 | + public static void Log(this ILogger logger, LogLevel logLevel, EventId eventId, Exception? exception, string? message, params object?[] args) |
| 27 | +``` |
| 28 | + |
| 29 | +显而易见,最后一个参数 `args` 就是格式化参数,类如 `string.format`。如果对内存有了解下的话,这里会出现装箱的操作,比如传参数的类型基本数据类型,就会发生装箱操作。 |
| 30 | +`.NET 6` 提供了 logger generation 的功能,它可以生成类型匹配的日志方法从而避免的装箱。 |
| 31 | + |
| 32 | +```csharp |
| 33 | +public static partial class Log |
| 34 | +{ |
| 35 | + [LoggerMessage(EventId = 23, Message = "{name} lives in {city}.")] |
| 36 | + public static partial void PlaceOfResidence(this ILogger logger, LogLevel logLevel, string name, string city); |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +生成器创建了如下的 `PlaceOfResidence` 方法 |
| 41 | + |
| 42 | +```csharp |
| 43 | +[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "7.0.7.1805")] |
| 44 | +public static partial void PlaceOfResidence(this global::Microsoft.Extensions.Logging.ILogger logger, global::Microsoft.Extensions.Logging.LogLevel logLevel, global::System.String name, global::System.String city) |
| 45 | +{ |
| 46 | + if (logger.IsEnabled(logLevel)) |
| 47 | + { |
| 48 | + logger.Log( |
| 49 | + logLevel, |
| 50 | + new global::Microsoft.Extensions.Logging.EventId(23, nameof(PlaceOfResidence)), |
| 51 | + new __PlaceOfResidenceStruct(name, city), |
| 52 | + null, |
| 53 | + __PlaceOfResidenceStruct.Format); |
| 54 | + } |
| 55 | +} |
| 56 | +``` |
| 57 | + |
| 58 | +2、[C# 12 介绍](https://pvs-studio.com/en/blog/posts/csharp/1074/) |
| 59 | + |
| 60 | + |
| 61 | + |
| 62 | +C# 12 即将发布,跟这篇文章看看有哪些新的内容 |
| 63 | + |
| 64 | +1. 主构造函数 |
| 65 | +2. 不同初始化集合的方法 |
| 66 | +3. 匿名方法中的默认参数 |
| 67 | +4. 类型别名 |
| 68 | +5. `nameof` 方法 |
| 69 | +6. 内联数组 |
| 70 | +7. 代码切入 |
| 71 | + |
| 72 | +3、[使用 Contains 而不是使用 IndexOf](https://dotnettips.wordpress.com/2023/10/25/microsoft-net-code-analysis-consider-using-string-contains-instead-of-string-indexof/) |
| 73 | + |
| 74 | + |
| 75 | + |
| 76 | +.NET Code Analyzer 另外一个建议是选择 `Contains` 而不是 `IndexOf` 来判断一个字符串是否在另一个字符串中。下面是 Benchmark 的结果,可以看出 `Contains` 的性能比 `IndexOf` 要好。 |
| 77 | + |
| 78 | + |
| 79 | + |
| 80 | +4、[自定义嵌入资源文件名](https://www.meziantou.net/customizing-the-embedded-resource-name-in-dotnet.htm) |
| 81 | + |
| 82 | + |
| 83 | + |
| 84 | +C# 支持将一个资源文件,比如图片,文本,数据当作资源作为程序集的一部分。默认的文件名为 `<assembly>.<namesapce>.<folder>` 作为文件的路径,但是 `C#` 还支持指定资源名或者做同意的替换 |
| 85 | + |
| 86 | +```xml |
| 87 | +<Project> |
| 88 | + <ItemGroup> |
| 89 | + <EmbeddedResource Include="Resources/**/*"> |
| 90 | + <LogicalName>$([System.String]::new('%(RelativeDir)').Replace('\','/'))%(FileName)%(Extension)</LogicalName> |
| 91 | + </EmbeddedResource> |
| 92 | + </ItemGroup> |
| 93 | +</Project> |
| 94 | +``` |
| 95 | + |
| 96 | +这样资源的名字就变成和 `Resource` 目录一致的格式。 |
| 97 | + |
| 98 | +5、[Pattern Matching 的新用法](https://www.youtube.com/shorts/Av-fv9EOrnw) |
| 99 | + |
| 100 | + |
| 101 | + |
| 102 | +模式匹配是 C# 的最近几个版本加入的功能,除了常见的 `switch` 语句查询,还有一个在 `if` 判断的条件中方式 |
| 103 | + |
| 104 | +```csharp |
| 105 | +var engineer = new Engineer() |
| 106 | +{ |
| 107 | + IsManager = false, |
| 108 | + IsSenior = true, |
| 109 | + Age = 30 |
| 110 | +}; |
| 111 | + |
| 112 | + |
| 113 | +if (engineer is |
| 114 | +{ |
| 115 | + IsManager: false, |
| 116 | + Age: >18 |
| 117 | +}) |
| 118 | +{ |
| 119 | + Console.WriteLine("Junior Engineer"); |
| 120 | +} |
| 121 | +``` |
| 122 | + |
| 123 | +它用来判断一个工程师对象的 `IsManager = false` 而且 `Age` 大于 18,则输出 `Junior Engineer` 的语句。 |
| 124 | + |
| 125 | +6、[.NET 8 中的 TimeProvider 类型](https://andrewlock.net/exploring-the-dotnet-8-preview-avoiding-flaky-tests-with-timeprovider-and-itimer/) |
| 126 | + |
| 127 | + |
| 128 | + |
| 129 | +千呼万唤始出来,`.NET 8` 终于提供了一个公开的时间接口,这样方便我们进行测试,它是一个叫做 `TimeProvider` 的抽象类, |
| 130 | + |
| 131 | +```csharp |
| 132 | +public abstract class TimeProvider |
| 133 | +{ |
| 134 | + public static TimeProvider System { get; } |
| 135 | + |
| 136 | + protected TimeProvider(); |
| 137 | + |
| 138 | + public virtual TimeZoneInfo LocalTimeZone { get; } |
| 139 | + public virtual long TimestampFrequency { get; } |
| 140 | + |
| 141 | + public DateTimeOffset GetLocalNow(); |
| 142 | + public virtual DateTimeOffset GetUtcNow(); |
| 143 | + public virtual long GetTimestamp(); |
| 144 | + public TimeSpan GetElapsedTime(long startingTimestamp); |
| 145 | + public TimeSpan GetElapsedTime(long startingTimestamp, long endingTimestamp); |
| 146 | + |
| 147 | + public virtual ITimer CreateTimer(TimerCallback callback, object? state,TimeSpan dueTime, TimeSpan period); |
| 148 | +} |
| 149 | +``` |
| 150 | + |
| 151 | +其中 `GetLocalNow()` 和 `GetUtcNow` 用来返回相应的时间。 |
| 152 | + |
| 153 | +7、[C# 性能比较的正确方式](https://sergeyteplyakov.github.io/Blog/benchmarking/2023/11/02/Performance_Comparison_For_Classes_vs_Structs.html) |
| 154 | + |
| 155 | + |
| 156 | + |
| 157 | +我们都知道使用 benchmark 来测算不同方式,数据类型的性能。但是在针对获取的数据时候,需要记住以下几点 |
| 158 | + |
| 159 | +1. 不要信任你不能解释的东西,尤其是微观上的benchmark |
| 160 | +2. 理解你测量的东西,不要着急下结论 |
| 161 | +3. 看到表面之下的东西,在性能分析之下,考虑更多抽象之下的内容 |
| 162 | +4. 要非常小心 Linq 的细节 |
| 163 | + |
| 164 | +8、[Visual Studio 中配置私有字段命名规则](https://ardalis.com/configure-visual-studio-to-name-private-fields-with-underscore/#sq_het4wkszkg) |
| 165 | + |
| 166 | + |
| 167 | + |
| 168 | +在 C# 中有一些明明规则,比如类和方法选择驼峰命名法,类中私有字段要求有下划线作为前缀,等等。所以在 `Visual Studio` 中配置相应的规则,可以帮助我们找到不符合规则的命名方式。 |
| 169 | + |
| 170 | + |
| 171 | +9、[Func, Predict 和 Expression 的区别](https://www.youtube.com/watch?v=PoniDOq5zQw&ab_channel=MilanJovanovi%C4%87) |
| 172 | + |
| 173 | + |
| 174 | + |
| 175 | +C# 中存在三种判断类型的委托,分别为 |
| 176 | + |
| 177 | +- `Func<T, bool>` |
| 178 | +- `Predicate<T>` |
| 179 | +- `Expression<Func<T, bool>>` |
| 180 | + |
| 181 | +三者的区别可以用这一段代码表示 |
| 182 | + |
| 183 | +```csharp |
| 184 | +Func<User, bool> userFunc = user => user.Age > 29; |
| 185 | + |
| 186 | +Predicate<User> userPredicate = user => user.Age > 29; |
| 187 | + |
| 188 | +Expression<Func<User, bool>> userEf = user => user.Age > 29; |
| 189 | + |
| 190 | +User user = new User { Age = 29 }; |
| 191 | + |
| 192 | +if (userFunc(user) |
| 193 | + || userPredicate(user) |
| 194 | + || userEf.Compile()(user)) |
| 195 | +{ |
| 196 | + |
| 197 | +} |
| 198 | +``` |
| 199 | + |
| 200 | +10、[Span](https://blog.ndepend.com/improve-c-code-performance-with-spant/) |
| 201 | + |
| 202 | + |
| 203 | + |
| 204 | + |
| 205 | +我们都知道 `Span` 类型能够提高 C# 代码运行的性能,那么它是究竟怎么做到的呢?这边文章给出了更加详细的解释。 |
0 commit comments