.NET 6 預覽版 5 發佈

很高興.NET 6 預覽版5終於跟大家見面了。我們現在正處於.NET 6 的後半部分,開始整合一些重要的功能。 例如.NET SDK 工作負載,它是我們.NET 統一願景的基礎,可以支持更多類型的應用程序。 與其他功能一樣,它也是為了更好地服務於端到端的用戶體驗。

您可以下載適用於Linux、macOS 和Windows 的.NET 6 預覽版5

請參閱ASP.NET CoreEF Core以及.NET MAUI,了解有關Web、數據訪問和跨平台UI 方案新增功能的更多詳細信息。

Visual Studio 2022 預覽版1 也在今天發佈,.NET 6 預覽版5包含其中。.NET 6 還在Visual Studio 16.11Visual Studio for Mac 8.9進行了測試。 如果您想在Visual Studio中試用.NET 6,我們建議您使用這些版本。

您也可以查看新的對話帖,深入了解工程師們對.NET新功能的想法。

.NET SDK:可選的工作負載改進

SDK 工作負載是一項新的.NET SDK 功能,它使我們能夠在不增加SDK 大小的情況下添加對新應用程序類型(如移動WebAssembly)的支持。

工作負載添加了包括list 和update 指令。這些新功能提供了與我們期望的最終體驗一致的使用感受。您將能夠使用一些簡單的指令快速構建您的環境,並一直將其保持最新狀態。

  • dotnet workload list 會告訴您安裝了哪些工作負載。
  • dotnet workload update 會將所有已安裝的工作負載更新到最新的可用版本。

update 指令會查詢nuget.org 以獲取更新的工作負載清單、更新本地清單、下載已安裝工作負載的新版本,然後刪除工作負載的所有舊版本。這類似於apt update 和apt upgrade -y(用於基於Debian 的Linux 發行版)。

dotnet workload 命令集在給定SDK 的上下文中運行。假設您同時安裝了.NET 6 和.NET 7。如果您同時使用兩者,則工作負載命令將提供不同的結果,因為工作負載會有所不同(至少是相同工作負載的不同版本)。

如您所見,工作負載功能本質上是.NET SDK 的包管理器。工作負載最初是在.NET 6 預覽版4版本中引入的。

.NET SDK:NuGet 包驗證

包驗證工具將使NuGet 庫開發人員能夠驗證他們的包是否一致且格式良好的。

這包括:

  • 驗證不同版本之間沒有重大更改。
  • 驗證包對於所有特定運行時具有相同的公共 API 集。
  • 識別任何與目標框架或運行時適用性之間的差距。

此工具可通過Microsoft.DotNet.PackageValidation獲得。

有關此工具的帖子很快就會發佈

.NET SDK:更多Roslyn 分析器

在.NET 5 中,我們隨.NET SDK 提供了大約250 個分析器。 其中許多已經存在,但我們將其作為NuGet 包另行發佈。 我們正在.NET 6 添加更多分析器

默認情況下,大多數新分析器應用在Info級別。您可以通過如下AnalysisMode的配置在Warning級別啟用這些分析器:

\<AnalysisMode\>AllEnabledByDefault\</AnalysisMode\>

我們發佈了我們想要的.NET 6 分析器集(加上一些額外的東西),然後將其中的大部分都公開了

預覽版5 中包含了Newell ClarkMeik Tranel的以下實現。請注意,社區用戶在之前的預覽版中貢獻了其他實現。

Contributor Issue Title
Newell Clark dotnet/runtime #33777 Use span-based string.Concat
Newell Clark dotnet/runtime #33784 Prefer string.AsSpan() over string.Substring() when parsing
Newell Clark dotnet/runtime #33789 Override Stream.ReadAsync/WriteAsync
Newell Clark dotnet/runtime #35343 Replace Dictionary<,>.Keys.Contains with ContainsKey
Newell Clark dotnet/runtime #45552 Use String.Equals instead of String.Compare
Meik Tranel dotnet/runtime #47180 Use String.Contains(char) instead of String.Contains(String)

.NET SDK:為平台兼容性分析器啟用自定義防護

CA1416 平台兼容性分析器已經使用OperatingSystem/RuntimeInformation 中的方法識別平台保護,例如OperatingSystem.IsWindows 和OperatingSystem.IsWindowsVersionAtLeast。 但是,分析器不識別任何其他保護可能性,例如緩存在字段或屬性中的平台檢查結果,或者在輔助方法中定義了複雜的平台檢查邏輯。

為了使自定義保護成為可能,我們添加了新屬性SupportedOSPlatformGuard 和UnsupportedOSPlatformGuard,可以使用相應的平台名稱和/或版本注釋自定義保護對象。 平台兼容性分析器的流分析邏輯識別並尊重這些注釋的內容。

用法

  [UnsupportedOSPlatformGuard("browser")] // The platform guard attribute
#if TARGET_BROWSER
    internal bool IsSupported => false;
#else
    internal bool IsSupported => true;
#endif

    [UnsupportedOSPlatform("browser")]
    void ApiNotSupportedOnBrowser() { }

    void M1()
    {
        ApiNotSupportedOnBrowser();  // Warns: This call site is reachable on all platforms.'ApiNotSupportedOnBrowser()' is unsupported on: 'browser'

        if (IsSupported)
        {
            ApiNotSupportedOnBrowser();  // Not warn
        }
    }

    [SupportedOSPlatform("Windows")]
    [SupportedOSPlatform("Linux")]
    void ApiOnlyWorkOnWindowsLinux() { }

    [SupportedOSPlatformGuard("Linux")]
    [SupportedOSPlatformGuard("Windows")]
    private readonly bool _isWindowOrLinux = OperatingSystem.IsLinux() || OperatingSystem.IsWindows();

    void M2()
    {
        ApiOnlyWorkOnWindowsLinux();  // This call site is reachable on all platforms.'ApiOnlyWorkOnWindowsLinux()' is only supported on: 'Linux', 'Windows'.

        if (_isWindowOrLinux)
        {
            ApiOnlyWorkOnWindowsLinux();  // Not warn
        }
    }
}

Windows Forms: 默認字體

您現在可以使用Application.SetDefaultFont 為應用程序設置默認字體。 您使用的模式類似於設置high dpi或視覺樣式。

class Program
{
    [STAThread]
    static void Main()
    {
        Application.SetHighDpiMode(HighDpiMode.SystemAware);
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

+       Application.SetDefaultFont(new Font(new FontFamily("Microsoft Sans Serif"), 8f));

        Application.Run(new Form1());
    }
}

下面是設置默認字體後的兩個例子(使用不同的字體)。

Microsoft Sans Serif, 8pt:

file

Chiller, 12pt:

file

.NET Core 3.0 中更新了默認字體。這一變化為一些用戶將.NET Framework 應用程序遷移到.NET Core 帶來了很大的阻礙。 默認字體設置使為應用程序選擇所需字體變得簡單,消除了遷移的障礙。

庫:放棄對舊框架的支持

從包中刪除框架是一個破壞源代碼的改動。但是,讓它一直支持我們發佈的所有框架也增加了包的複雜性和大小。過去,我們通過_harvesting_來解決這個問題,也就是:

  • 我們只為”當前”支持的框架做構建
  • 在構建期間,我們下載包的早期版本,並切除我們不再構建的早期框架的二進制文件

雖然這意味着您可以隨時更新而不必擔心我們會刪除框架,但這也意味着如果您使用切割過的二進制文件,您將永遠不會獲得任何錯誤修復或新功能。換句話說,切割過的無法提供服務的資產,現在已經被隱藏了,因為從您的角度來看,您可以繼續將包更新到更高版本,即使您正在使用我們不再更新的舊二進制文件。

從.NET 6 Preview 5 開始,我們計劃不再執行任何形式的harvesting,以確保我們為交付的所有內容都得到更好的服務。這意味着我們將放棄對任何早於以下版本的支持:

  • .NET Framework 4.6.1
  • .NET Core 3.1
  • .NET Standard 2.0

如果您當前正在引用來自早期框架的受影響的包,您將無法再將引用的包更新到更高版本。您可以將您的項目重新指向到更高版本的框架或選擇不更新引用的包(這通常不是一個大的回退,因為無論如何您已經在使用凍結的二進制文件)。

更多詳細信息,包括受影響軟件包的完整列表,請參閱dotnet/announcement:刪除舊框架版本

庫:Microsoft.Extensions

我們一直在改進此版本的Microsoft.Extensions API。 在預覽版5 中,我們專註於託管和依賴注入。 在預覽版4 中,我們添加了一個用於日誌記錄的編譯時源生成器

感謝Martin Björkström](//github.com/bjorkstromm) 的dotnet/runtime #51840 (AsyncServiceScope)

託管 – ConfigureHostOptions API

我們在IHostBuilder 上添加了一個新的ConfigureHostOptions API 以簡化應用程序配置(例如配置超時關閉):

using HostBuilder host = new()
    .ConfigureHostOptions(o =>
    {
        o.ShutdownTimeout = TimeSpan.FromMinutes(10);
    })
    .Build();

host.Run();

在預覽版5 之前,配置更加複雜一些:

using HostBuilder host = new()
    .ConfigureServices(services =>
    {
        services.Configure<HostOptions>(o =>
        {
            o.ShutdownTimeout = TimeSpan.FromMinutes(10);
        });
    })
    .Build();

host.Run();

依賴注入 – CreateAsyncScope API

您可能已經注意到,當服務提供者註冊了IAsyncDisposable 服務時,其銷毀會拋出InvalidOperationException。

新的CreateAsyncScope API 提供了一個很直接的解決方案,如以下示例所示:

await using (var scope = provider.CreateAsyncScope())
{
    var foo = scope.ServiceProvider.GetRequiredService<Foo>();
}

以下示例演示了現有的問題案例,並演示了此前建議的解決方法。

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

await using var provider = new ServiceCollection()
        .AddScoped<Foo>()
        .BuildServiceProvider();

// This using can throw InvalidOperationException
using (var scope = provider.CreateScope())
{
    var foo = scope.ServiceProvider.GetRequiredService<Foo>();
}

class Foo : IAsyncDisposable
{
    public ValueTask DisposeAsync() => default;
}

您可以通過將返回的scope強制轉換為IAsyncDisposable 來繞過異常。

var scope = provider.CreateScope();
var foo = scope.ServiceProvider.GetRequiredService<Foo>();
await ((IAsyncDisposable)scope).DisposeAsync();

CreateAsyncScope 解決了這個問題,使您可以安全地使用using 語句。

庫:JsonSerializer 源碼生成器

幾乎所有.NET 序列化工具的支柱都是反射機制。反射對於某些場景來說是一個很棒的功能,但不適用於高性能雲原生應用程序(因其通常需要處理大量序列化反序列化工作和JSON 文檔)。過多的反射使用會引起一些程序啟動、內存使用和程序集修整上的問題。

運行時反射的替代方法是編譯時源代碼生成。源碼生成器生成C# 源文件,這些文件可以作為庫或應用程序構建的一部分進行編譯。在編譯時生成源代碼可以為.NET 應用程序提供許多好處,包括性能的提升。

在.NET 6 中,我們在System.Text.Json 中引入了一個新的源碼生成器。JSON 源碼生成器與JsonSerializer 協同工作,並且可以配置成多種多樣的工作方式。是否使用新的源碼生成器由您決定。它可以提供以下好處:

  • 減少啟動時間
  • 提高序列化吞吐量
  • 減少私有內存使用
  • 移除運行時System.Reflection 和System.Reflection.Emit 的使用
  • 允許兼容trim的JSON 序列化

例如,源碼生成器可以生成對properties更簡單有效的賦值/取值的代碼,而不是在運行時通過Reflection.Emit生成get和set方法(這會用到私有內存並且影響啟動速度),使得性能得到了更大的提高。

您可以使用System.Text.Json NuGet 包的最新預覽版來試用源碼生成器。我們正在提議在SDK 中包含源碼生成器。

生成優化的序列化邏輯

默認情況下,JSON 源碼生成器為給定的可序列化類型應用序列化邏輯。這比直接使用現有JsonSerializer 來生成使用 Utf8JsonWriter 的源碼提供了更高的性能。 簡而言之,源生成器提供了一種在編譯時應用的不同實現方法,以優化程序的運行時體驗。

宏觀來講,JsonSerializer 是一個強大的工具,它有許多可以改進.NET 類型和JSON 格式之間序列化和反序列化的功能(甚至還有更多即將實現的新功能!)。 它速度很快,但是當序列化例程只需要一個功能子集時,它也會產生一些性能開銷。 展望未來,我們將同時更新JsonSerializer 和新的源碼生成器。

給定一個簡單類:

namespace Test
{
    internal class JsonMessage
    {
        public string Message { get; set; }
    }
}

源生成器可以配置為為JsonMessage 類的實例生成序列化邏輯。 請注意,類名JsonContext 是任意的。 您可以為生成的源碼使用任何您想要的類名。

using System.Text.Json.Serialization;

namespace Test
{
    [JsonSerializable(typeof(JsonMessage)]
    internal partial class JsonContext : JsonSerializerContext
    {
    }
}

我們通過JsonSerializerOptionsAttribute 定義了一組JsonSerializer 功能,這些功能由提供最佳序列化吞吐量的源碼生成模式提供。 這些功能可以提前指定給源碼生成器,以避免在運行時進行額外檢查。 如果該屬性未聲明,運行時會使用默認的JsonSerializationOptions。

作為構建的一部分,源碼生成器用以下內容擴充JsonContext 部分類:

internal partial class JsonContext : JsonSerializerContext
{
    public static JsonContext Default { get; }

    public JsonTypeInfo<JsonMessage> JsonMessage { get; }

    public JsonContext(JsonSerializerOptions options) { }

    public override JsonTypeInfo GetTypeInfo(Type type) => ...;
}

使用此模式的序列化程序調用可能類似於以下示例。此示例提供了可能的最佳性能。

using MemoryStream ms = new();
using Utf8JsonWriter writer = new(ms);

JsonContext.Default.JsonMessage.Serialize(writer, new JsonMessage { "Hello, world!" });
writer.Flush();

// Writer contains:
// {"Message":"Hello, world!"}

或者,您可以繼續使用JsonSerializer,並且使用JsonContext.Default.JsonMessage 將生成代碼的實例傳遞給它。

JsonSerializer.Serialize(jsonMessage, JsonContext.Default.JsonMessage);

這是一個類似的用法,但重載方法不同。

JsonSerializer.Serialize(jsonMessage, typeof(JsonMessage), JsonContext.Default);

這兩個重載之間的區別在於,第一個重載使用類型化元數據實現 — JsonTypeInfo&lt;T&gt; — 第二個使用更通用的無類型化實現,該實現執行類型測試以確定上下文實例中是否存在類型化實現。因此,它有點慢(由於類型測試)。如果給定類型沒有源碼生成的實現,則序列化程序會拋出NotSupportedException。但是它不會回退到基於反射的實現(我們的設計明確了這一點)。

基於Utf8JsonWriter的最快、最優化的源碼生成模式目前僅可用於序列化。我們會根據您的反饋決定是否在未來提供基於Utf8JsonReader的反序列化實現。

但是,源碼生成器還提供類型元數據初始化邏輯,這也有助於反序列化。要使用預先生成的類型元數據反序列化JsonMessage 的實例,您可以執行以下操作:

JsonSerializer.Deserialize(json, JsonContext.Default.JsonMessage);

類似於上面的序列化,你也可以這樣寫:

JsonSerializer.Deserialize(json, typeof(JsonMessage), JsonContext.Default);

補充筆記

  • 可以通過派生的部分JsonSerializerContext 實例上的[JsonSerializable] 包含多種類型以用於源生成,不止支持一個。
  • 源生成器還支持嵌套對象和集合成員,而不僅僅是原始類型。

庫:WebSocket 壓縮

壓縮對於通過網絡傳輸的任何數據都很重要。WebSockets 現在啟用壓縮。我們使用了WebSockets 的permessage-deflate 擴展實現,RFC 7692。它允許使用DEFLATE 算法壓縮WebSockets 消息有效負載。

此功能是用戶對GitHub 上Networking 的最高要求之一。您可以通過API 審查1API 審查2跟隨我們提供該API 的過程。

歸功於Ivan Zlatanov。謝謝Ivan!

我們意識到將壓縮與加密結合使用可能會導致攻擊,例如CRIMEBREACH。這意味着不能在單個壓縮上下文中將秘密與用戶生成的數據一起發送,否則可以提取該秘密。為了讓用戶注意這些影響並幫助他們權衡風險,我們將API 重命名為DangerousDeflateOptions。我們還添加了對特定消息關閉壓縮的功能,因此如果用戶想要發送秘密,他們可以在不壓縮的情況下安全地發送。

Ivan還進行了一項後續工作,當禁用壓縮時,WebSocket 的內存佔用減少了約27%。

從客戶端啟用壓縮很容易,請參見下面的示例。但是,請記住服務器可以協商設置,例如請求較小的窗口,或完全拒絕壓縮。

var cws = new ClientWebSocket();
cws.Options.DangerousDeflateOptions = new WebSocketDeflateOptions()
{
    ClientMaxWindowBits = 10,
    ServerMaxWindowBits = 10
};

最近還添加了對ASP.NET Core 的WebSocket 壓縮支持。 它將包含在即將發佈的預覽中。

庫:Socks 代理支持

SOCKS是一種代理服務器實現,可以處理任何TCP 或UDP 流量,使其成為一個非常通用的系統。 這是一個長期存在的社區請求,已添加到.NET 6 中

此更改增加了對Socks4、Socks4a 和Socks5 的支持。 例如,它允許通過SSH 測試外部連接或連接到Tor 網絡

WebProxy 類現在接受socks 方案,如下例所示。

var handler = new HttpClientHandler
{
    Proxy = new WebProxy("socks5://127.0.0.1", 9050)
};
var httpClient = new HttpClient(handler);

歸功於Huo Yaoyuan。謝謝Huo!

庫:支持OpenTelemetry 指標

作為我們關注可觀察性的一部分,我們一直在為最近的幾個.NET 版本添加對OpenTelemetry 的支持。在.NET 6 中,我們添加了對OpenTelemetry Metrics API支持。通過添加對OpenTelemetry的支持,您的應用程序可以與其他OpenTelemetry 系統無縫互操作。

System.Diagnostics.MetricsOpenTelemetry Metrics API 規範的.NET 實現。Metrics API 是專門為處理原始測量而設計的,通常旨在高效且同時地生成這些測量的連續摘要。

API 包括可用於創建儀器對象(例如計數器)的Meter 類。API 公開了四個儀器類:Counter、Histogram、ObservableCounter 和ObservableGauge,以支持不同的指標場景。此外,API 公開MeterListener 類以允許偵聽儀器記錄的測量以進行聚合和分組。

OpenTelemetry .NET 實現將擴展為使用這些新API,這些API 增加了對Metrics 可觀察性場景的支持。

庫測量記錄示例

Meter meter = new Meter("io.opentelemetry.contrib.mongodb", "v1.0");
    Counter<int> counter = meter.CreateCounter<int>("Requests");
    counter.Add(1);
    counter.Add(1, KeyValuePair.Create<string, object>("request", "read"));

聽力示例

  MeterListener listener = new MeterListener();
    listener.InstrumentPublished = (instrument, meterListener) =>
    {
        if (instrument.Name == "Requests" && instrument.Meter.Name == "io.opentelemetry.contrib.mongodb")
        {
            meterListener.EnableMeasurementEvents(instrument, null);
        }
    };
    listener.SetMeasurementEventCallback<int>((instrument, measurement, tags, state) =>
    {
        Console.WriteLine($"Instrument: {instrument.Name} has recorded the measurement {measurement}");
    });
    listener.Start();

庫:BigInteger 性能

從十進制和十六進制字符串解析BigIntegers 已得到改進。 我們看到了高達89% 的改進,如下圖所示。

file

歸功於Joseph Da Silva。 謝謝Joseph!

庫:Vector&lt;T&gt; 現在支持nint 和nuint

Vector&lt;T&gt; 現在支持在C# 9 中添加的nint 和nuint 基元類型。例如,此更改應該可以更輕鬆地使用具有指針或平台相關長度的SIMD 指令。

庫:支持OpenSSL 3

.NET 加密API 支持使用OpenSSL 3作為Linux 上的首選本機加密提供程序。 如果可用,.NET 6 將使用OpenSSL 3。 否則,它將使用OpenSSL 1.x。

庫:添加支持ChaCha20/Poly1305 加密算法

ChaCha20Poly1305 類已添加到System.Security.Cryptography。 要使用ChaCha20/Poly1305算法,必須得到底層操作系統的支持。 靜態IsSupported 屬性可用於確定在給定上下文中是否支持該算法。

  • Linux:需要OpenSSL 1.1 或更高版本。
  • Windows:構建20142 或更高版本(目前需要開發”內部人員”頻道)

感謝Kevin Jones對Linux 的支持。 謝謝Kevin!

互操作性:Objective-C 互操作性支持

該團隊一直在添加Objective-C 支持,目標是為.NET 提供單一的Objective-C 互操作實現。 到目前為止,Objective-C互操作系統是圍繞Mono 嵌入API 構建的,但我們認為這不是跨運行時共享的正確方法。 因此,我們創建了一個新的.NET API,它將支持單一的Objective-C 互操作體驗,最終將在兩個運行時上運行。

這個用於Objective-C 互操作的新API 為NSAutoreleasePool的兩個運行時帶來了即時支持,從而支持Cocoa 的引用計數內存管理系統。 您現在可以配置是否希望每個託管線程都具有隱式NSAutoreleasePool。 這使得在每個線程的基礎上釋放Cocoa 對象成為可能。

診斷(EventPipe/DiagnosticsServer)——MonoVM

從.NET 6 開始,MonoVM 中添加了許多診斷功能。這啟用了託管EventSource/EventListener、EventPipe 和DiagnosticsServer 等功能。 它支持使用診斷工具,如dotnet-trace、dotnet-counters、dotnet-stacks,用於在移動設備(iOS/Android) 和桌面上運行的應用程序。

這些新功能開啟了在PrefView/SpeedScope/Chromium、dotnet-trace等工具中分析MonoVM 生成的nettrace 文件的能力,或使用TraceEvent等庫編寫自定義解析器

我們將繼續包含更多功能,主要側重於SDK 集成並將更多本地運行時事件(Microsoft-Windows-DotNETRuntime) 適配到MonoVM 中,從而在nettrace 文件中啟用更多事件。

現已具備以下功能:

  • 在MonoVM 和CoreCLR 之間共享本機EventPipe/DiagnosticsServer 庫
  • 將TCP/IP 支持添加到DiagnosticsServer 並利用該配置構建MonoVM iOS/Android 運行時包。需要以支持移動平台。
  • BCL EventSources 在MonoVM 上運行,將事件發送到EventPipe。
  • System.Diagnostics.Tracing.RuntimeEventSource 發出的BCL 運行時計數器連接到MonoVM,可從dotnet-counters 等工具消耗。
  • 自定義事件源在MonoVM 上運行,將自定義事件發送到EventPipe,可通過dotnet-trace 等工具使用。
  • 自定義事件計數器在MonoVM 上運行,將自定義計數器事件發送到EventPipe,可通過dotnet-counters 等工具使用。
  • 示例分析器在MonoVM 上實現,將事件發送到EventPipe。開啟了使用dotnet-trace 在MonoVM 上進行CPU 分析的能力。
  • dotnet-dsrouter 診斷工具的實現,允許使用現有的診斷工具,如dotnet-trace、dotnet-counters、dotnet-stack 以及在移動目標上運行的MonoVM,無需更改現有工具。dotnet-dsrouter 運行本地IPC 服務器,將所有流量從診斷工具路由到在模擬器/設備上的MonoVM 中運行的DiagnosticsServer。
  • 使用基於組件的架構在MonoVM 中實現EventPipe/DiagnosticsServer。
  • 基於文件會話的診斷環境的實現/擴展。

iOS CPU 採樣(SpeedScope)

下圖演示了在SpeedScope 中查看的iOS 啟動CPU 採樣會話的一部分。

file

Android CPU 採樣(PerfView)

下圖演示了在PerfView(無限睡眠中的主線程)中查看的Android CPU 採樣。
file

運行時:CodeGen

RyuJIT 中進行了以下更改。

社區貢獻

感謝@SingleAccretion的這些貢獻。

動態PGO

file

JIT 循環優化

LSRA

在”分配寄存器”表中包含寄存器選擇啟發式//github.com/dotnet/runtime/pull/52513新舊錶的差異:

file

保持結構在註冊

優化調試經驗

file

SIMD

涉及SIMD 或HWIintrinsics 的某些方法的內聯現在應該具有改進的代碼生成和性能。 我們看到了高達95% 的改進

file

結束

就功能的廣度和數量而言,.NET 6 預覽版5 可能是迄今為止最大的預覽版。 您可以使用源生成器和分析器查看Roslyn 功能對低級庫功能的影響程度。 未來真的到來了。 我們現在擁有一個非常強大的編譯器工具鏈,使我們能夠生成高度優化和正確的代碼,並為您自己的項目提供完全相同的體驗。

現在是開始測試.NET 6 的好時機。現在我們根據您的反饋採取行動還為時過早。 很難想像,雖然我們要到2021 年11 月才會發佈,但反饋窗口很快就會縮小到僅針對高嚴重性問題。 該團隊提前進行了大約一年半的預覽,並將很快轉向主要關注質量問題。 如果可以,請嘗試.NET 6。

感謝您成為.NET 開發人員。