November 5th, 2023

.NET 8 RC2现已推出

Mia Wu
Partner Technical Advisor

本篇翻译于Jon Douglas的Announcing .NET 8 Release Candidate 2

.NET 8 RC2 现已推出。这是我们最后一个候选版本。此版本包含 .NET 包的新 NuGet 包自述文件、适用于 MSBuild 的基于 CLI 的简单项目评估、将容器发布到 tar.gz 存档以及适用于 .NET 的 Tensor Primitives等内容。

.NET Conf 2023的日期已公布!请于2023 年 11 月 14 日至 16 日同我们一起庆祝.NET 8 发布会的开幕!

下载适用于 Linux、macOS 和 Windows 的.NET 8 RC2

⚠️重要提示: 由于 Razor 工具存在依赖性错误,.NET SDK 8.0.100-rc.2必须与 Visual Studio 17.8 Preview 3 一起使用。更多详情请参阅 SDK 公告

您还可以查看另外一些令人兴奋的内容:

.NET 库的新包自述文件

包使用者面临的最大使用问题是缺乏文档。因此,我们正在努力提高 NuGet 包自述文件的采用率和质量。

自述文件是包的重要组成部分,因为它为用户提供了重要信息,并帮助他们快速了解包是什么以及它的用途。另外,用户在 NuGet.org 以及其他工具上查看包时首先要做的事情就是查看自述文件。对于包作者来说,为他们的包编写并包含高质量的自述文件至关重要。

现在有以下 Microsoft 包的自述文件:

状态
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Logging
Microsoft.Extensions.DependencyInjection.Abstractions
Microsoft.Extensions.Hosting
Microsoft.Extensions.Hosting.WindowsServices
Microsoft.Extensions.Logging.Abstractions
Microsoft.Extensions.Http
System.IO.Ports
System.Data.OleDb
Microsoft.Extensions.Options
System.Management
Microsoft.Extensions.Options.ConfigurationExtensions
Microsoft.Extensions.Caching.Memory
Microsoft.Extensions.Logging.Console
Microsoft.Extensions.Hosting.Abstractions
System.Text.Encoding.CodePages
Microsoft.Bcl.AsyncInterfaces
System.DirectoryServices.AccountManagement
System.Speech
System.DirectoryServices
Microsoft.Extensions.Logging.Debug
System.Net.Http.Json
System.Data.Odbc
Microsoft.Extensions.Primitives
Microsoft.Bcl.Numerics (new package)
Microsoft.Bcl.TimeProvider (new package)
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Abstractions
Microsoft.Extensions.Configuration.Binder
Microsoft.Extensions.Logging.EventLog
System.Diagnostics.EventLog
System.Net.Http.WinHttpHandler
System.Text.Json
System.Threading.Channels

MSBuild:基于 CLI 的简单项目评估

MSBuild 是一个非常强大的平台,非常擅长将其他生态系统的数据和工具集成到它的世界观中。它历来都不太擅长向更广泛的脚本和工具世界提供它所拥有的数据。在过去,工具作者必须执行一些操作,例如注入自己的 MSBuild 代码读取某些属性、项目或目标输出到文件中,然后解析这些文件。这项工作非常容易出错。在 .NET 8 中,MSBuild 团队提供了一项新功能,可以更轻松地将 MSBuild 中的数据合并到脚本或工具中。让我们看一个示例进行了解。

>dotnet publish --getProperty:OutputPath
bin\Release\net8.0\

在这个简单的示例中,在运行publish命令后,我们使用 –getProperty 请求 OutputPath 属性的值, MSBuild 已将单个属性的值写入我的终端的标准输出中。这对于 CI 管道之类的工具非常有用,以前您可能需要硬编码输出路径,现在您可以直接由数据驱动! 让我们看一个更复杂的示例,其中我们获取多个属性。在这个示例中,我们使用 .NET SDK 为项目发布容器镜像,我们希望使用在这个过程中生成的容器的一些属性:

>dotnet publish -p PublishProfile=DefaultContainer --getProperty:GeneratedContainerDigest --getProperty:GeneratedContainerConfiguration
{
  "Properties": {
    "GeneratedContainerDigest": "sha256:ef880a503bbabcb84bbb6a1aa9b41b36dc1ba08352e7cd91c0993646675174c4",
    "GeneratedContainerConfiguration": "{\u0022config\u0022:{\u0022ExposedPorts\u0022:{\u00228080/tcp\u0022:{}},\u0022Labels\u0022:{\u0022org.opencontainers.image.created\u0022:\u00222023-10-02T18:20:01.6356453Z\u0022,\u0022org.opencontainers.artifact.created\u0022:\u00222023-10-02T18:20:01.6356453Z\u0022,\u0022org.opencontainers.artifact.description\u0022:\u0022A project that demonstrates publishing to various container registries using just\\r\\n      the .NET SDK\u0022,\u0022org.opencontainers.image.description\u0022:\u0022A project that demonstrates publishing to various container registries using just\\r\\n      the .NET SDK\u0022,\u0022org.opencontainers.image.authors\u0022:\u0022Chet Husk\u0022,\u0022org.opencontainers.image.url\u0022:\u0022https://github.com/baronfel/sdk-container-demo\u0022,\u0022org.opencontainers.image.documentation\u0022:\u0022https://github.com/baronfel/sdk-container-demo\u0022,\u0022org.opencontainers.image.version\u0022:\u00221.0.0\u0022,\u0022org.opencontainers.image.licenses\u0022:\u0022MIT\u0022,\u0022org.opencontainers.image.title\u0022:\u0022.NET SDK 7 Container Demo\u0022,\u0022org.opencontainers.image.base.name\u0022:\u0022mcr.microsoft.com/dotnet/aspnet:8.0.0-rc.1\u0022},\u0022Env\u0022:[\u0022PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\u0022,\u0022APP_UID=1654\u0022,\u0022ASPNETCORE_HTTP_PORTS=8080\u0022,\u0022DOTNET_RUNNING_IN_CONTAINER=true\u0022,\u0022DOTNET_VERSION=8.0.0-rc.1.23419.4\u0022,\u0022ASPNET_VERSION=8.0.0-rc.1.23421.29\u0022],\u0022WorkingDir\u0022:\u0022/app\u0022,\u0022Entrypoint\u0022:[\u0022dotnet\u0022,\u0022sdk-container-demo.dll\u0022],\u0022User\u0022:\u00221654\u0022},\u0022created\u0022:\u00222023-10-02T18:20:04.6914310Z\u0022,\u0022rootfs\u0022:{\u0022type\u0022:\u0022layers\u0022,\u0022diff_ids\u0022:[\u0022sha256:d310e774110ab038b30c6a5f7b7f7dd527dbe527854496bd30194b9ee6ea496e\u0022,\u0022sha256:379caff0dd639afb033e114cb8da17c334a36d0c6c01bb4cf5f2d1a811968742\u0022,\u0022sha256:80627b9413613b9703eec6adc7a3a751ac1b7571041c77899456345f823ef63a\u0022,\u0022sha256:6231bc2ccd45860760c09a50d6d059aa4b6aa357e41e6d06f4394f24176f203d\u0022,\u0022sha256:56fa0b94fd5e406124a3d070ec79998698ddea2e635bf53bbf106dc86aeaa240\u0022,\u0022sha256:272eedde5582036a7f26fe5d069f4ba328ba7a5c6be30f6dcbee9838224df148\u0022,\u0022sha256:4b8ab71658cccccfaf8979b1025f3ed1b12e936a448dcd13b9ab4f7709f31357\u0022]},\u0022architecture\u0022:\u0022amd64\u0022,\u0022os\u0022:\u0022linux\u0022,\u0022history\u0022:[{\u0022created\u0022:\u00222023-09-20T04:55:40.8154909Z\u0022,\u0022created_by\u0022:\u0022/bin/sh -c #(nop) ADD file:a1398394375faab8dd9e1e8d584eea96c750fb57ae4ffd2b14624f1cf263561b in / \u0022},{\u0022created\u0022:\u00222023-09-20T04:55:41.1203677Z\u0022,\u0022created_by\u0022:\u0022/bin/sh -c #(nop)  CMD [\\u0022bash\\u0022]\u0022,\u0022empty_layer\u0022:true},{\u0022comment\u0022:\u0022buildkit.dockerfile.v0\u0022,\u0022created\u0022:\u00222023-09-20T12:13:34.5265068Z\u0022,\u0022created_by\u0022:\u0022ENV APP_UID=1654 ASPNETCORE_HTTP_PORTS=8080 DOTNET_RUNNING_IN_CONTAINER=true\u0022,\u0022empty_layer\u0022:true},{\u0022comment\u0022:\u0022buildkit.dockerfile.v0\u0022,\u0022created\u0022:\u00222023-09-20T12:13:34.5265068Z\u0022,\u0022created_by\u0022:\u0022RUN /bin/sh -c apt-get update     \\u0026\\u0026 apt-get install -y --no-install-recommends         ca-certificates                 libc6         libgcc-s1         libicu72         libssl3         libstdc\\u002B\\u002B6         tzdata         zlib1g     \\u0026\\u0026 rm -rf /var/lib/apt/lists/* # buildkit\u0022},{\u0022comment\u0022:\u0022buildkit.dockerfile.v0\u0022,\u0022created\u0022:\u00222023-09-20T12:13:34.8536814Z\u0022,\u0022created_by\u0022:\u0022RUN /bin/sh -c groupadd         --gid=$APP_UID         app     \\u0026\\u0026 useradd -l         --uid=$APP_UID         --gid=$APP_UID         --create-home         app # buildkit\u0022},{\u0022comment\u0022:\u0022buildkit.dockerfile.v0\u0022,\u0022created\u0022:\u00222023-09-20T12:13:36.8764982Z\u0022,\u0022created_by\u0022:\u0022ENV DOTNET_VERSION=8.0.0-rc.1.23419.4\u0022,\u0022empty_layer\u0022:true},{\u0022comment\u0022:\u0022buildkit.dockerfile.v0\u0022,\u0022created\u0022:\u00222023-09-20T12:13:36.8764982Z\u0022,\u0022created_by\u0022:\u0022COPY /dotnet /usr/share/dotnet # buildkit\u0022},{\u0022comment\u0022:\u0022buildkit.dockerfile.v0\u0022,\u0022created\u0022:\u00222023-09-20T12:13:37.1287440Z\u0022,\u0022created_by\u0022:\u0022RUN /bin/sh -c ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet # buildkit\u0022},{\u0022comment\u0022:\u0022buildkit.dockerfile.v0\u0022,\u0022created\u0022:\u00222023-09-20T12:13:39.8264990Z\u0022,\u0022created_by\u0022:\u0022ENV ASPNET_VERSION=8.0.0-rc.1.23421.29\u0022,\u0022empty_layer\u0022:true},{\u0022comment\u0022:\u0022buildkit.dockerfile.v0\u0022,\u0022created\u0022:\u00222023-09-20T12:13:39.8264990Z\u0022,\u0022created_by\u0022:\u0022COPY /shared/Microsoft.AspNetCore.App /usr/share/dotnet/shared/Microsoft.AspNetCore.App # buildkit\u0022},{\u0022author\u0022:\u0022.NET SDK\u0022,\u0022created\u0022:\u00222023-10-02T18:20:04.6914069Z\u0022,\u0022created_by\u0022:\u0022.NET SDK Container Tooling, version 8.0.100-dev\u0022}]}"
  }
}

在这里,我们请求了两个属性,并收到了一个带有Properties 属性的JSON 对象,其中包含我们请求的两个属性(MSBuild 将这两个属性存储为字符串)。从这里,我们可以使用CLI 的任何JSON工具或我们选择的编程语言来解析并使用这些数据。

同样的行为也适用于 MSBuild 项目,可以使用 –getItem:<ITEMTYPE> 按项目类型请求该项目。MSBuild 会将这些作为 JSON 对象返回到项目名称键下的数组中,所有这些都位于“Items”键下,就像在本例中我们请求为镜像发布的所有镜像标签一样:

>dotnet publish -p PublishProfile=DefaultContainer --getItem:ContainerImageTags
{
  "Items": {
    "ContainerImageTags": [
      {
        "Identity": "latest",
        .... other MSBuild Item Metadata elided ....
    ]
  }
}

最后,我们还支持使用 –getTargetResults:<TARGETNAME> 来检索已运行的给定目标的输出。这些输出在 TargetResults 属性下返回,就像在本示例中,我们获得生成的 SDK 容器镜像所使用的最终镜像名称:

>dotnet publish -p PublishProfile=DefaultContainer --getTargetResult:ComputeContainerBaseImage
{
  "TargetResults": {
    "ComputeContainerBaseImage": {
      "Result": "Success",
      "Items": [
        {
          "Identity": "mcr.microsoft.com/dotnet/aspnet:8.0.0-rc.1",
          .... other MSBuild Item Metadata elided ....
        }
      ]
    }
  }
}

我们认为这会让 MSBuild 更容易集成到其他工具中,所以我们很高兴看到大家开始使用它!

SDK Container Publish:发布到tar.gz存档

SDK 已经能够将容器发布到本地容器工具(例如 Docker 和 Podman)以及远程容器注册表(例如 Azure Container Registry、Amazon’s Elastic Container Registry和 Docker Hub)有一段时间了。 然而,并非所有工作流程都如此简单。 例如,有些团队可能更喜欢在推送镜像之前对镜像运行扫描工具。为了帮助支持这些工作流程,社区成员 @Danielku15 为 SDK Container Publish 工具实现了一项很棒的新功能即直接以tar.gz归档文件的形式创建容器。一旦存档完成,它就可以根据您的需求移动、扫描或加载到本地 Docker 工具链中。 我们来看看它有多简单:

>dotnet publish -p PublishProfile=DefaultContainer -p ContainerArchiveOutputPath=./images/sdk-container-demo.tar.gz
MSBuild version 17.8.0+6cdef4241 for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
C:\Program Files\dotnet\sdk\8.0.100-rc.2.23477.19\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.RuntimeIdentifierInference.targets(311,5): message NETSDK1057: You are using a preview version of .NET. See: http
s://aka.ms/dotnet-support-policy [D:\code\sdk-container-demo\src\sdk-container-demo\sdk-container-demo.csproj]
  sdk-container-demo -> D:\code\sdk-container-demo\src\sdk-container-demo\bin\Release\net8.0\sdk-container-demo.dll
  sdk-container-demo -> D:\code\sdk-container-demo\src\sdk-container-demo\bin\Release\net8.0\publish\
  Building image 'sdk-container-demo' with tags 'latest' on top of base image 'mcr.microsoft.com/dotnet/aspnet:8.0.0-rc.1'.
  Pushed image 'sdk-container-demo:latest' to local archive at 'D:\code\sdk-container-demo\src\sdk-container-demo\images\sdk-container-demo.tar.gz'.

这里,我在publish命令中添加了一个新属性:ContainerArchiveOutputPath。在指定此属性后,工具不会将镜像推送到 Docker 或远程注册表,而是创建我选择的文件。 在本例中,我明确指定了一个文件名,但如果您愿意,也可以指定一个文件夹来将镜像推送到其中。 如果这样做,生成的 tar.gz 将以镜像命名(因此在本例中生成的名称将为 sdk-container-demo.tar.gz)。

一旦您获得 tar.gz 后,您可以把他移动到任何需要的位置,或者简单地使用 docker load 将其加载到 Docker 中:

>docker load --input .\images\sdk-container-demo.tar.gz
5d6d1d62da4b: Loading layer [==================================================>]  9.442MB/9.442MB
Loaded image: sdk-container-demo:latest

就像我的镜像可以通过 Docker 运行一样。我们希望这项新功能能够为更多团队带来新的工作流程 – 请在我们的存储库中提出您的建议!

.NET Tensor Primitives简介

在过去的几年中,.NET 对数字支持进行了重大改进,努力优化AI和Machine Learning等数据密集型工作负载。

从 .NET Core 3 中添加硬件内部函数开始,我们为 .NET 开发人员提供了访问硬件特定指令的权限。这使得 .NET 应用程序能够利用现代硬件功能,如矢量指令,这对AI工作负载至关重要。

这项工作的下一步是 Generic Math,它在 .NET 6 中作为预览功能引入,并在 .NET 7 中标记为稳定。Generic Math 提供了更多类型安全的简化数字运算。此功能无需创建大量几乎相同的实现来满足不同的数字类型,从而简化了代码并使其更易于维护。

Tensor Primitives 是 .NET 中基于硬件内部函数和通用数学构建的AI 数值演进的下一步,从 RC2 开始,这组功能就包含在 .NET 8 中。

Tensor Primitives 是 System.Numerics.Tensors.TensorPrimitives 的缩写,是一组新的 API,引入了对tensor运算的支持。 TensorPrimitives API 通过独立的 System.Numerics.Tensors NuGet 包提供。 此包的内容取代了之前仅作为预览版提供的 System.Numerics.Tensors 包。在 .NET 8 中,System.Numerics.Tensors 将被标记为稳定。

有关更多详细信息,请参阅数字和AI的未来提案

语义搜索示例

语义搜索或检索增强生成 (RAG) 等AI工作负载通过将相关数据合并到提示中,增强了ChatGPT 等大型语言模型的自然语言功能。 在这些任务中,向量操作(例如余弦相似度)对于识别最相关的数据来回答问题至关重要。

假设您有一个包含电影标题和嵌入的电影数据库。 嵌入不在本文的讨论范围内,但它们是将语义信息编码为数字数组的方法。在本例中,它们代表了这些电影情节的简要概要,并且它们是预先计算过的。但是,如果您愿意,可以使用 Azure OpenAI 等模型来生成嵌入

var movies = new [] {
    new {Title="The Lion King", Embedding= new [] {0.10022575f, -0.23998135f}},
    new {Title="Inception", Embedding= new [] {0.10327095f, 0.2563685f}},
    new {Title="Toy Story", Embedding= new [] {0.095857024f, -0.201278f}},
    new {Title="Pulp Function", Embedding= new [] {0.106827796f, 0.21676421f}},
    new {Title="Shrek", Embedding= new [] {0.09568083f, -0.21177962f}}
};

假设您想使用搜索词“适合全家人一起观看的电影”来搜索适合家庭观看的电影。 该查询的嵌入可能如下所示。

var queryEmbedding = new[] {0.12217915f, -0.034832448f };

此时,为了查看数据库中的哪些电影与我的查询更匹配,我可以使用余弦相似度这样的距离函数来计算距离。

Tensor Primitives之前

TensorPrimitives 出现之前,如果您需要对tensor形状的数据应用任何操作(例如余弦相似度),您有两种选择:

  1. 使用外部依赖项
  2. 实现您自己的操作

使用外部依赖项

您使用这些操作的最简单方法是使用现有库,例如 TorchSharp、TensorFlow.NET、Math.NET Numerics、ML.NET 或 Semantic Kernel。 如果您正在构建的应用程序严重依赖于这些库提供的功能,那么将它们添加到您的项目中是有意义的。但是,如果您需要的只是这些库中的一些方法,那么依赖这些库会带来额外的开销。

using TorchSharp;
using static TorchSharp.torch.nn.functional;

var top3MoviesTorchSharp = 
    movies
        .Select(movie => 
            new {
                Title=movie.Title, 
                Embedding=movie.Embedding, 
                Similarity=cosine_similarity(torch.tensor(queryEmbedding), torch.tensor(movie.Embedding), 0L).item<float>()})
        .OrderByDescending(movies => movies.Similarity)
        .Take(3);

以上就是衡量我的查询与我的电影集合的相似性的代码。按最相似度排序(越高越相似)并选出最有可能“适合家庭观看”的前 3 部电影后,输出如下:

标题 相似度
Toy Story 0.66102695
Shrek 0.6457999
The Lion King 0.62360466

正如你从这个例子中看到的,像《Pulp Fiction》和《Inception》这样的电影没有出现在结果中,因为它们不被认为是“适合家庭”的电影。

虽然 TorchSharp 让您可以轻松计算余弦相似度,但将其添加为该函数的依赖项可能没有意义。

编写您自己的实现

如果您不想使用另一个外部依赖项来使用应用程序中的某些操作,则另一个选择是实现您自己的依赖项。这就是前面提到的某些库所做的。 虽然这是一条可行的路径,可以为您提供对代码的最大控制权,但这意味着您正在编写框架级别的代码,而不是专注于应用程序或业务的竞争优势。 您的代码很可能是该操作的简单实现,没有充分利用运行时的硬件优化功能。

public float CosineSimilarityCustom(ReadOnlySpan<float> vector1, ReadOnlySpan<float> vector2)
{
    if (vector1.Length != vector2.Length)
        throw new ArgumentException("Vectors must have the same length");

    float dotProduct = 0f;
    float magnitude1 = 0f;
    float magnitude2 = 0f;

    for (int i = 0; i < vector1.Length; i++)
    {
        dotProduct += vector1[i] * vector2[i];
        magnitude1 += vector1[i] * vector1[i];
        magnitude2 += vector2[i] * vector2[i];
    }

    magnitude1 = MathF.Sqrt(magnitude1);
    magnitude2 = MathF.Sqrt(magnitude2);

    if (magnitude1 == 0 || magnitude2 == 0)
        return 0;  // handle the case where one or both vectors have zero magnitude

    return dotProduct / (magnitude1 * magnitude2);
}

这就是余弦相似度的自定义实现。然后您可以按如下方式使用它。

var top3MoviesCustom = 
    movies
        .Select(movie => 
            new {
                Title=movie.Title, 
                Embedding=movie.Embedding, 
                Similarity=CosineSimilarityCustom(queryEmbedding, movie.Embedding)})
        .OrderByDescending(movies => movies.Similarity)
        .Take(3);

这段代码的结果与 TorchSharp 示例中的结果相同。虽然在这种情况下没有外部依赖项,但余弦相似度现在成为您需要维护的一组框架级代码。

Tensor Primitives之后

TensorPrimitives 简化了这些选择。如果您只需要少量操作,那么不需要在项目中加入大量的外部依赖项。

using System.Numerics.Tensors;

var top3MoviesTensorPrimitives = 
    movies
        .Select(movie => 
            new {
                Title=movie.Title, 
                Embedding=movie.Embedding, 
                Similarity=TensorPrimitives.CosineSimilarity(queryEmbedding, movie.Embedding)})
        .OrderByDescending(movies => movies.Similarity)
        .Take(3);

同样,对于使用 Semantic Kernel 和 ML.NET 等操作的库来说,他们可以用 TensorPrimitives 替换许多现有的实现,这样它们就可以专注于自己的竞争优势并更快地交付功能。

当前状态

目前,TensorPrimitives为以下操作提供了矢量化实现:

  • CosineSimilarity
  • SoftMax
  • Sigmoid
  • Tanh
  • Sinh
  • Norm (L2)
  • SumOfSquares
  • ProductOfSums

有关操作的完整列表,请参阅跟进中的Tensor Primitives工作项问题

建议用法

在最初的测试中,我们观察到该包引入的性能效率在具有大量元素的tensor上更为明显。

下一步计划?

在接下来的几个月里,我们计划:

  • 提供更全面的tensor操作覆盖。
  • 将NET 和 Semantic Kernel 等库中这些操作的现有实现替换为Tensor Primitives 实现。

现在就开始使用Tensor Primitives

在您的 .NET 项目中,请添加对最新 System.Numeric.Tensors NuGet 包的引用。

dotnet add package System.Numeric.Tensors --prerelease

请向我们提供反馈并在 dotnet/runtime 存储库中提交问题。

社区贡献者

Florian Verdonck

我是Florian Verdonck,住在比利时的佛兰德斯地区。

我的第一次编程是在18岁的时候。VB.NET 是我的第一门编程语言,在我的高等教育即将结束时,我更喜欢 C# 作为我的首选语言。我很快就对 Web 开发产生了兴趣但并不专门从事前端或后端开发。随着我职业生涯的发展,我一直渴望学习新的软件和实践。在掌握了 C# 和面向对象编程的窍门后,我在当地的一次聚会上了解了函数式编程并进行了尝试。

探索函数式编程让我大开眼界。它拓宽了我对软件行业许多事情的看法,例如使用流行的编程语言时间过长会让您忘记其他语言以及为什么时不时走出舒适区很重要。

探索 F# 来了解函数式编程对我来说是一个合乎逻辑的步骤,因为我对 .NET 非常熟悉。 我喜欢 F# 代码的简洁性,并且它不需要从头开始就可以实现功能,您可以从您所知道的开始,并在学习新技术时逐渐应用它们。F# 还拥有一款独一无二具有小功能和隐藏宝石的宝藏手套。我爱上了F#。

由于 F# 是我的第一种基于缩进的编程语言,因此我花了一些时间来适应它的语法。 就像我在其他语言中所做的那样,有一样东西是我所缺少的,那就是代码格式化器。

当我要向共享代码库添加新代码时,我很重视代码风格的一致性,并且总是尽我最大努力为其他人着想。当我开始时,F# 没有任何开箱即用的解决方案来解决多个协作者的问题,有时他们具有自己的风格,无法共享代码,因为它根本没有以普遍理解的方式格式化。这就是我开始为 Fantomas 项目做贡献的原因,作为 F# 基金会指导计划的一部分。在该项目失去主要维护者后,我的导师 Anthony Lloyd和我重新启动了该项目。

为 Fantomas 做出贡献对我的职业生涯很有帮助。它不仅让我获得了一些真实的 F# 工作经验,而且还引导我追求其他兴趣,如公开演讲、产品所有权和社区建设。 维护 Fantomas 是我对 F# 生态系统的贡献,在这个过程中,我找到了人生意义和归属感。

我对 Fantomas 的热情有实际的一面,我和大多数个人开源维护者一样在业余时间研究它,但在当时看来,它不是一个真正可靠或稳定的工具。 每个角落都隐藏着令人沮丧的错误,采用率有限,并且 F# 编译器中存在上游短缺(Fantomas 在内部使用 F# 编译器的解析器)。

Fantomas 的崛起得益于伦敦金融科技公司 G-Research 的支持。 G-Research 的开源部门看到了该项目的潜力,并决定资助我在 Fantomas 上的工作。这使得我可以每周都为 Fantomas 正式工作。

G-Research 的赞助让我有时间解决代码库中更具挑战性的问题,并促使我开始为 F# 编译器做出贡献。在 Chet Husk 的帮助下,我成功提交了我的第一个 Pull 请求,并解决了解析后缺乏信息的问题。 一旦克服了最初为编译器做出贡献的心理障碍,我就开始一次又一次地做这件事。F# 团队感谢并帮助我做出的所有贡献,我鼓励每个人都尝试做出贡献!

去年,我全职加入了 G-Research 的开源团队。不仅致力于 Fantomas,还解决 F# 编译器和工具中更大的问题。它是一家愿意积极为其使用的软件和工具做出贡献的公司。我非常幸运能够有幸参与这些有趣的项目,并有机会参与 F# 生态系统的核心工作。

反馈的重要性

最后,我们想强调在 .NET 8 预览版和 RC 版本中获取您的反馈的重要性。您的反馈有助于塑造新的 .NET 体验。下面举一些例子来说明您反馈的重要性:

总结

在 .NET 8 发布的过程中,我们向世界各地所有热情的 .NET 开发人员表示衷心的感谢,感谢他们积极探索和测试预览版和候选版本。您对 .NET 生态系统的奉献是无价的,您的反馈对于确保最新版本的可靠性和稳健性发挥了关键作用。感谢您成为这一激动人心的旅程中必不可少的一部分,我们迫不及待地想看看您开始使用.NET 8。

如果大家有任何的技术问题,欢迎到我们的官方的.NET中文论坛 提问。

Author

Mia Wu
Partner Technical Advisor

0 comments