|
| 1 | +# .NET 每周分享第 43 期 |
| 2 | + |
| 3 | +## 卷首语 |
| 4 | + |
| 5 | +C# 的受欢迎指数超过 Java |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +最近的市场调研显示,C# 受欢迎指数正在接近并超越 Java |
| 10 | + |
| 11 | + |
| 12 | +## 行业资讯 |
| 13 | + |
| 14 | +1、[Jetbrains .NET Day 录像](https://blog.jetbrains.com/dotnet/2023/10/02/recordings-jetbrains-dotnet-day-online-23/) |
| 15 | + |
| 16 | +今年 JetBrains 的 .NET Day 活动录像。 |
| 17 | + |
| 18 | +2、[VS Code 插件 Dev Kit GA](https://devblogs.microsoft.com/dotnet/csharp-dev-kit-now-generally-available/) |
| 19 | + |
| 20 | + |
| 21 | + |
| 22 | +在 `VS Code` 中 `C#` 开发插件 `Dev Kit` 已经可以公开使用。 |
| 23 | + |
| 24 | + |
| 25 | +## 文章推荐 |
| 26 | + |
| 27 | +1、[ASP.NET Core 学习路线](https://roadmap.sh/aspnet-core) |
| 28 | + |
| 29 | + |
| 30 | + |
| 31 | +ASP.NET Core 学习路线,包含了四种类型 |
| 32 | +1. 个人建议 |
| 33 | +2. 可选 |
| 34 | +3. 不是严格 |
| 35 | +4. 不建议 |
| 36 | + |
| 37 | +2、[.NET Roll Forward 配置](https://weblog.west-wind.com/posts/2023/Oct/02/Rolling-Forward-to-Major-Versions-in-NET) |
| 38 | + |
| 39 | + |
| 40 | + |
| 41 | +得益于 `.NET` 的兼容性,`.NET 6` 编写的应用程序可以运行在 `.NET 7` 的运行时中,那么该如何配置这个呢? |
| 42 | + |
| 43 | +```xml |
| 44 | +<Project Sdk="Microsoft.NET.Sdk"> |
| 45 | + <PropertyGroup> |
| 46 | + <TargetFramework>net7.0-windows</TargetFramework> |
| 47 | + <RollForward>LatestMajor</RollForward> |
| 48 | + <PropertyGroup> |
| 49 | +</Project> |
| 50 | +``` |
| 51 | + |
| 52 | +除了 `LastMajor` 外,还有 |
| 53 | + |
| 54 | +- `Minor` |
| 55 | +- `Major` |
| 56 | +- `LatestMinor` |
| 57 | + |
| 58 | +等配置。如果是 `Preview` 版本的 SDK 该怎么办呢?比如 `net8.0.0-rc-123419.4`, 则需要配置 `DOTNET_ROLL_FORWARD_TO_PRERELEASE` 环境变量 |
| 59 | + |
| 60 | +```powershell |
| 61 | +$env:DOTNET_ROLL_FORWARD_TO_PRERELEASE=1 |
| 62 | +``` |
| 63 | + |
| 64 | + |
| 65 | +3、[序列化中的未知枚举类型](https://gaevoy.com/2023/09/26/dotnet-serialization-unknown-enums-handling-api.html) |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | +在序列化成对象的时候,如果为 Enum (枚举) 类型中不存在的类型时候,通常会抛出异常,那么在不同序列化场景中怎么解决好这个问题呢?分别为 `Newtonsoft`, `System.Text.Json` 和 `System.Xml.Serializaton` 等库给出了示例。 |
| 70 | + |
| 71 | +4、[如何在 UT 中测试 Logger](https://www.meziantou.net/how-to-test-the-logs-from-ilogger-in-dotnet.htm) |
| 72 | + |
| 73 | + |
| 74 | + |
| 75 | +在单元测试用该如何测试 `ILogger` 对象呢?有很多方法,比如 |
| 76 | +- 使用 `Mock` 库来构造 `ILogger` 实现 |
| 77 | +- 将所有日志保存在内存中,然后比较 |
| 78 | +- 将所有日志保存在文件中 |
| 79 | + |
| 80 | +第一种方式比较直接,也是大众最为熟悉的方式,第二种就比较不常见,但是有现成的库能够帮助我们实现, `Meziantou.Extensions.Logging.InMemory` |
| 81 | + |
| 82 | +```csharp |
| 83 | +// Create the logger |
| 84 | +using var loggerProvider = new InMemoryLoggerProvider(); |
| 85 | +var logger = loggerProvider.CreateLogger("MyLogger"); |
| 86 | + |
| 87 | +// Call the method to test |
| 88 | +MethodToTest(logger); |
| 89 | + |
| 90 | +// Validate the logs |
| 91 | +Assert.Empty(loggerProvider.Logs.Errors); |
| 92 | +Assert.Single(loggerProvider.Logs, log => log.Message.Contains("test") && log.EventId.Id == 1); |
| 93 | +``` |
| 94 | + |
| 95 | +5、[用内存映射文件](https://blog.stephencleary.com/2023/09/memory-mapped-files-overlaid-structs.html) |
| 96 | + |
| 97 | + |
| 98 | + |
| 99 | +在 `C#` 这样托管的编程语言中,我们也可以使用访问内存的方式来修改文件内容,这样可以频繁的避免磁盘读写操作,操作系统自会需要的时候才会进行磁盘读写。那么该怎么做呢? |
| 100 | + |
| 101 | +```csharp |
| 102 | +using FileStream file = new FileStream(@"tmp.dat", FileMode.Create, FileAccess.ReadWrite, |
| 103 | + FileShare.None, 4096, FileOptions.RandomAccess); |
| 104 | +using MemoryMappedFile mapping = MemoryMappedFile.CreateFromFile(file, null, 1000, |
| 105 | + MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, leaveOpen: true); |
| 106 | +using MemoryMappedViewAccessor view = mapping.CreateViewAccessor(); |
| 107 | +``` |
| 108 | + |
| 109 | +这里创建一个`view` 对象,它代表了一个内存指针,这个对象包含了很多个操作方法,通常我们不需要关心,可以直接创建一个 `Overlay` 的对象来操作它 |
| 110 | + |
| 111 | +```csharp |
| 112 | +public sealed unsafe class Overlay : IDisposable |
| 113 | +{ |
| 114 | + private readonly MemoryMappedViewAccessor _view; |
| 115 | + private readonly byte* _pointer; |
| 116 | + |
| 117 | + public Overlay(MemoryMappedViewAccessor view) |
| 118 | + { |
| 119 | + _view = view; |
| 120 | + view.SafeMemoryMappedViewHandle.AcquirePointer(ref _pointer); |
| 121 | + } |
| 122 | + |
| 123 | + public void Dispose() => _view.SafeMemoryMappedViewHandle.ReleasePointer(); |
| 124 | + |
| 125 | + public ref T As<T>() where T : struct => ref Unsafe.AsRef<T>(_pointer); |
| 126 | +} |
| 127 | +``` |
| 128 | + |
| 129 | +通过它可以方位 `T` 类型的结构 |
| 130 | + |
| 131 | +```csharp |
| 132 | +public struct Data |
| 133 | +{ |
| 134 | + public int First; |
| 135 | + public int Second; |
| 136 | +} |
| 137 | + |
| 138 | +using Overlay overlay = new Overlay(view); |
| 139 | +ref Data data = ref overlay.As<Data>(); |
| 140 | +data.First = 1; |
| 141 | +data.Second = 2; |
| 142 | +``` |
| 143 | + |
| 144 | + |
| 145 | + |
| 146 | +6、[本地运行 .NET 的 LLM](https://blog.maartenballiauw.be/post/2023/06/15/running-large-language-models-locally-your-own-chatgpt-like-ai-in-csharp.html) |
| 147 | + |
| 148 | + |
| 149 | + |
| 150 | +随着 LLM 的流行,我们特别希望能够一款运行在本地的大模型。Meta 公司的 LLaMA 开源模型给了这件事情的可能性,SciSharp 项目就是将机器学习和 AI 框架引入到 .NET 生态中。这里介绍如何使用 `C#` 实现本地大模型 |
| 151 | + |
| 152 | + |
| 153 | +## 开源项目 |
| 154 | + |
| 155 | +1、[YamlDotNet](https://github.com/aaubry/YamlDotNet) |
| 156 | + |
| 157 | + |
| 158 | + |
| 159 | +很多时候,我们需要进行将 `Yaml` 和 `C#` 中的对象进行转换,`YamlDotNet` 库可以很方便的进行序列化和反序列化。 |
| 160 | + |
| 161 | + |
| 162 | +```csharp |
| 163 | +using YamlDotNet.Serialization; |
| 164 | +using YamlDotNet.Serialization.NamingConventions; |
| 165 | +... |
| 166 | + |
| 167 | + var person = new Person |
| 168 | +{ |
| 169 | + Name = "Abe Lincoln", |
| 170 | + Age = 25, |
| 171 | + HeightInInches = 6f + 4f / 12f, |
| 172 | + Addresses = new Dictionary<string, Address>{ |
| 173 | + { "home", new Address() { |
| 174 | + Street = "2720 Sundown Lane", |
| 175 | + City = "Kentucketsville", |
| 176 | + State = "Calousiyorkida", |
| 177 | + Zip = "99978", |
| 178 | + }}, |
| 179 | + { "work", new Address() { |
| 180 | + Street = "1600 Pennsylvania Avenue NW", |
| 181 | + City = "Washington", |
| 182 | + State = "District of Columbia", |
| 183 | + Zip = "20500", |
| 184 | + }}, |
| 185 | + } |
| 186 | +}; |
| 187 | + |
| 188 | +var serializer = new SerializerBuilder() |
| 189 | + .WithNamingConvention(CamelCaseNamingConvention.Instance) |
| 190 | + .Build(); |
| 191 | +var yaml = serializer.Serialize(person); |
| 192 | +System.Console.WriteLine(yaml); |
| 193 | +// Output: |
| 194 | +// name: Abe Lincoln |
| 195 | +// age: 25 |
| 196 | +// heightInInches: 6.3333334922790527 |
| 197 | +// addresses: |
| 198 | +// home: |
| 199 | +// street: 2720 Sundown Lane |
| 200 | +// city: Kentucketsville |
| 201 | +// state: Calousiyorkida |
| 202 | +// zip: 99978 |
| 203 | +// work: |
| 204 | +// street: 1600 Pennsylvania Avenue NW |
| 205 | +// city: Washington |
| 206 | +// state: District of Columbia |
| 207 | +// zip: 20500 |
| 208 | +``` |
| 209 | + |
| 210 | + |
| 211 | +2、[Source Code Generator Playgroud](https://github.com/davidwengier/SourceGeneratorPlayground) |
| 212 | + |
| 213 | + |
| 214 | + |
| 215 | + |
| 216 | +Source Generator 是 C# 的引入和的新功能,那么这个在线工具可以测试该功能。 |
0 commit comments