原文:bit.ly/2VJxjxQ
做者:Richard
翻譯:精緻碼農-王亮
說明:文中有大量的超連接,這些連接在公衆號文章中被自動剔除,一部分包含超連接列表的小段落被我刪減了,若是你對此感興趣,請參考閱讀原文。linux
咱們很高興地發佈了 .NET 6 預覽版 7。這是咱們進入(兩個)候選發佈版(RC)以前的最後一個預覽版。在咱們放慢發佈速度以前,團隊一直在螢窗雪案,以完成最後一組功能。在這個版本中,你將看到各功能的最後一次拋光,一次到位地整合整個版本的大型功能。今後時起,團隊將專一於使全部的功能達到統一的(高)質量,以便 .NET 6 爲你的生產工做作好準備。ios
關於生產工做的話題,值得提醒你們的是,.NET 官網[1]和 Bing.com 從預覽版 1 開始就一直運行在 .NET 6 上。咱們正在與不一樣的團隊(微軟和其餘公司)商談有關進入生產的 .NET 6 RC 版本。若是你對此感興趣,並但願獲得相關的指導,請聯繫 dotnet@microsoft.com
。咱們始終很樂意與早期採用者交流。git
你能夠在這下載[2] Linux、macOS 和 Windows 的 .NET 6 預覽版 7。github
- 安裝程序和二進制文件 https://dotnet.microsoft.com/download/dotnet/6.0 - 容器鏡像 https://hub.docker.com/_/microsoft-dotnet - Linux 包 https://github.com/dotnet/core/blob/main/release-notes/6.0/install-linux.md - 發佈說明 https://github.com/dotnet/core/blob/main/release-notes/6.0/README.md - API 差別 https://github.com/dotnet/core/tree/main/release-notes/6.0/preview/api-diff/preview7 - 已知問題 https://github.com/dotnet/core/blob/main/release-notes/6.0/known-issues.md - GitHub issue 跟蹤 https://github.com/dotnet/core/issues/6554
請參閱 .NET MAUI[3] 和 ASP.NET Core[4],瞭解更多關於客戶端和 Web 應用場景的新內容。web
.NET 6 預覽版 7 已經在 Visual Studio 2022 預覽版 3 中測試經過並獲得支持。Visual Studio 2022 使你可以利用爲 .NET 6 開發的 Visual Studio 工具,如 .NET MAUI 的開發、C# 應用程序的 Hot Reload、WebForms 的 Web Live,以及 IDE 體驗中的其餘性能改進。Visual Studio Code 也支持 .NET 6。算法
請查看咱們新的對話帖[5],瞭解工程師之間關於最新的 .NET 功能的深刻討論。咱們還發表了關於 C# 10 中的字符串插值和 .NET 6 中的預覽功能 - 泛型 Math[6]。docker
咱們更新了 .NET SDK 的模板,以使用最新的 C# 語言特性和模式。咱們已經有一段時間沒有在新的語言特性方面從新審視這些模板了。如今是時候了,咱們將確保模板在將來使用新的功能。json
如下是新模板中使用的語言特性:api
using
指令(經過 SDK 驅動的默認值)new
表達式你可能會問,爲何咱們要經過模板啓用某些功能,而不是在項目以 .NET 6 爲 Target 時默認啓用這些功能。儘管咱們能夠要求你在升級應用程序到新版本的 .NET 時作一些工做,做爲改善平臺默認行爲的交換條件,這使咱們可以改進產品,而不會使項目文件隨着時間的推移而變得複雜。然而,有些功能對於這種模式來講多是至關具備破壞性的,好比可空(Nullable)的引用類型。不管是在何時,咱們都不想把這些功能與升級體驗聯繫在一塊兒,而是想把這個選擇權留給你。模板是一個風險更低的支點,在那裏咱們可以爲新的代碼設置新的「好的默認模型」,而不會產生那麼多下游的後果。經過項目模板啓用這些功能,咱們獲得了一箭雙鵰的結果:新代碼開始時啓用了這些功能,但現有的代碼在你升級時不會受到影響。數組
控制檯模板變化最大,經過頂層語句和全局引用指令,它如今是一個單行代碼:
// See https://aka.ms/new-console-template for more information Console.WriteLine("Hello, World!");
而之前的同一模板的 .NET 5 版本是這樣的:
using System; namespace Company.ConsoleApplication1 { class Program { static void Main(string[] args) { Console.WriteLine("Hello, World!"); } } }
控制檯模板的項目文件也發生了變化,啓用了可空(Nullable)引用類型的功能,例如:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <Nullable>enable</Nullable> </PropertyGroup> </Project>
其餘模板也能夠實現可空(Nullable)引用類型、隱式全局引用和 File-scoped 命名空間,包括 ASP.NET Core 和 類庫。
Web 模板也一樣減小了代碼行數,使用一樣的功能:
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.MapGet("/", () => "Hello World!"); app.Run();
MVC 模板的結構也相似。在這種狀況下,咱們將 Program.cs
和 Startup.cs
合併爲一個文件(Program.cs
),造成了進一步的簡化:
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
關於使用新模板的兼容性問題,請參見如下內容:
可空引用類型[9]是編寫可靠代碼的一個重要特徵。它在編寫代碼時很是有用,但在檢查代碼時卻沒有(直到如今)。新的反射 API[10] 使你可以肯定一個給定方法的參數和返回值的可空性。這些新的 API 對於基於反射的工具和序列化來講是相當重要的,好比說:
就上下文而言,咱們在 .NET 5 中爲 .NET 庫添加了可空標註[11](在.NET 6 中完成),而且正在爲 ASP.NET Core 的這個版本作一樣的工做。咱們也看到開發者在他們的項目中採用了可空性(Nullability)[12]。
可空性(Nullability)信息存在於使用自定義屬性的元數據中[13]。原則上,任何人都已經能夠讀取自定義屬性,然而,這並不理想,由於編碼的消耗非同小可。
下面的例子演示了在幾個不一樣的場景中使用新的 API。
想象一下,你正在實現一個序列化器。使用這些新的 API,序列化器能夠檢查一個給定的屬性是否能夠被設置爲 null
:
private NullabilityInfoContext _nullabilityContext = new NullabilityInfoContext(); private void DeserializePropertyValue(PropertyInfo p, object instance, object? value) { if (value is null) { var nullabilityInfo = _nullabilityContext.Create(p); if (nullabilityInfo.WriteState is not NullabilityState.Nullable) { throw new MySerializerException($"Property '{p.GetType().Name}.{p.Name}'' cannot be set to null."); } } p.SetValue(instance, value); }
可空性對能夠持有其餘對象的對象有特殊處理,好比數組和元組。例如,你能夠指定一個數組對象(做爲一個變量,或者做爲一個類型成員簽名的一部分)必須是非空的,可是元素能夠是空的,或者相反。這種額外的特殊性是能夠經過新的反射 API 來檢查的,例如:
class Data { public string?[] ArrayField; public (string?, object) TupleField; } private void Print() { Type type = typeof(Data); FieldInfo arrayField = type.GetField("ArrayField"); FieldInfo tupleField = type.GetField("TupleField"); NullabilityInfoContext context = new (); NullabilityInfo arrayInfo = context.Create(arrayField); Console.WriteLine(arrayInfo.ReadState); // NotNull Console.WriteLine(arrayInfo.Element.State); // Nullable NullabilityInfo tupleInfo = context.Create(tupleField); Console.WriteLine(tupleInfo.ReadState); // NotNull Console.WriteLine(tupleInfo.GenericTypeArguments [0].State); // Nullable Console.WriteLine(tupleInfo.GenericTypeArguments [1].State); // NotNull }
System.IO.Compression.ZipFile
類如今能夠在建立過程當中捕獲 Unix 文件權限,並在相似 Unix 的操做系統上提取壓縮文件時設置文件權限。這一變化容許可執行文件在壓縮包中被循環使用,這意味着你再也不須要修改文件權限來使文件在解壓縮包後可執行。它也一樣遵循 user
、group
和 other
讀/寫權限。
若是一個壓縮包不包含文件權限(由於它是在 Windows 上建立的,或者使用了一個沒有捕獲權限的工具,好比早期的 .NET 版本),那麼解壓縮的文件就會獲得默認的文件權限,就像其餘新建立的文件同樣。
Unix 的文件權限也適用於其餘壓縮工具,包括:
對於 .NET 6,咱們已經創建了將 API 標記爲「預覽」的能力[14]。這種新方法將使咱們可以在多個主要版本中提供和發展預覽功能。爲了使用預覽 API,項目須要明確選擇使用的預覽功能。若是你在沒有明確選擇的狀況下使用預覽功能,從 .NET 6 RC1 開始,你會看到帶有可操做信息的構建錯誤。預覽功能預計將在之後的版本中發生變化,可能會有破壞性的變化。這就是爲何他們要選擇加入。
咱們在 .NET 6 中預覽的這些功能之一是靜態抽象接口成員。這些容許你在接口中定義靜態的抽象方法(包括操做符)。例如,如今能夠實現代數泛型方法。對於一些人來講,這個功能將是咱們今年交付的絕對突出的改進。這也許是自 Span<T>
以來最重要的新類型系統特性。
下面的例子是一個 IEnumerable<T>
,因爲 T
被限制爲 INumber<T>
,多是一個 INumber<int>
,因此可以對全部的數值進行求和。
public static T Sum<T>(IEnumerable<T> values) where T : INumber<T> { T result = T.Zero; foreach (var value in values) { result += value; } return result; }
這是由於 INumber<T>
定義了各類(靜態)操做符重載,必須由接口實現者來知足。IAdditionOperators
也許是最容易理解的新接口,INumber<T>
自己就是派生自這個接口。
這都是由一個新的功能提供的,它容許在接口中聲明靜態抽象成員。這使得接口能夠公開運算符和其餘靜態方法,好比 Parse
或 Create
,而且這些方法能夠由派生類型實現。更多細節請參見咱們的相關博文[15]。
全部提到的功能都是 .NET 6 的預覽版,不支持在生產中使用。咱們將感謝您在使用中提供反饋。咱們打算在 .NET 7 中繼續發展和改進泛型 Math 功能以及支持它們的運行時和 C# 功能。咱們但願對當前的體驗進行突破性的改變,這也是爲何新的 API 被標記爲「預覽」的部分緣由。
咱們增長了新的本地內存分配 API[16],經過 System.Runtime.InteropServices.NativeMemory
公開。這些 API 至關於 C 語言中的 malloc
、free
、realloc
和 calloc
API,還包括用於進行對齊分配的 API。
你可能想知道如何看待這些 API。首先,它們是低級別的 API,是爲低級別的代碼和算法準備的。應用程序開發人員不多會用到這些。另外一種思考這些 API 的方式相似於平臺內部的 API,它們是用於 CPU 指令的低級別 .NET API。這些 API 是相似的,但它是爲內存相關的操做暴露的低級別的 API。
System.Text.Json
序列化器如今將通知做爲(反)序列化操做的一部分公開。它們對於默認值和驗證很是有用。要使用它們,請在 System.Text.Json.Serialization
命名空間中實現一個或多個接口 IJsonOnDeserialized
、IJsonOnDeserializing
、IJsonOnSerialized
或 IJsonOnSerializing`。
這裏有一個例子,在 JsonSerializer.Serialize()
和 JsonSerializer.Deserialize()
中都進行驗證,以確保 FirstName
屬性不是 null
。
public class Person : IJsonOnDeserialized, IJsonOnSerializing { public string FirstName{ get; set; } void IJsonOnDeserialized.OnDeserialized() => Validate(); // Call after deserialization void IJsonOnSerializing.OnSerializing() => Validate(); // Call before serialization private void Validate() { if (FirstName is null) { throw new InvalidOperationException("The 'FirstName' property cannot be 'null'."); } } }
之前,你須要實現一個自定義轉換器來實現這一功能。
咱們使用 System.Text.Json.Serialization.JsonPropertyOrderAttribute
特性增長了控制屬性序列化順序的能力,用一個整數指定了順序,較小的整數先被序列化;沒有該特性的屬性有一個默認的排序值 0
。
這裏有一個例子,指定 JSON 應該按照 Id
, City
, FirstName
, LastName
的順序進行序列化:
public class Person { public string City { get; set; } // No order defined (has the default ordering value of 0) [JsonPropertyOrder(1)] // Serialize after other properties that have default ordering public string FirstName { get; set; } [JsonPropertyOrder(2)] // Serialize after FirstName public string LastName { get; set; } [JsonPropertyOrder(-1)] // Serialize before other properties that have default ordering public int Id { get; set; } }
之前,序列化順序是由反射順序決定的,而反射順序既不是肯定的,也不會致使特定的預期順序。
在用 Utf8JsonWriter
編寫 JSON payloads 時,有時你須要嵌入「原始」JSON。
好比:
JsonWriterOptions writerOptions = new() { WriteIndented = true, }; using MemoryStream ms = new(); using UtfJsonWriter writer = new(ms, writerOptions); writer.WriteStartObject(); writer.WriteString("dataType", "CalculationResults"); writer.WriteStartArray("data"); foreach (CalculationResult result in results) { writer.WriteStartObject(); writer.WriteString("measurement", result.Measurement); writer.WritePropertyName("value"); // Write raw JSON numeric value using FormatNumberValue (not defined in the example) byte[] formattedValue = FormatNumberValue(result.Value); writer.WriteRawValue(formattedValue, skipValidation: true); writer.WriteEndObject(); } writer.WriteEndArray(); writer.WriteEndObject();
如下是對上述代碼--特別是FormatNumberValue
--的描述。爲了提升性能,System.Text.Json
在數字爲整數時省略了小數點/值,如 1.0
。其理由是,寫的字節數越少越好,有利於提升性能。在某些狀況下,保留小數點可能很重要,由於消費者將沒有小數點的數字視爲整數,不然視爲浮點數。這種新的「原始值」模型容許你在任何須要的地方擁有這種程度的控制。
咱們爲 JsonSerializer
添加了新的同步 API[17],用於將 JSON 數據序列化和反序列化到一個流。你能夠在下面的例子中看到這個演示。
using MemoryStream ms = GetMyStream(); MyPoco poco = JsonSerializer.Deserialize<MyPoco>(ms);
這些新的同步 API 包括與新的 System.Text.Json source generator
[18] 兼容和可用的重載,經過接受 JsonTypeInfo<T>
或 JsonSerializerContext
實例。
在過去的幾年裏,咱們一直在改進對 OpenTelemetry
[19] 的支持。實現該支持的一個關鍵點是確保全部須要參與遙測生產的組件以正確的格式輸出到網絡頭。要作到這一點真的很難,特別是隨着 OpenTelemetry
規範的變化。OpenTelemetry
定義了傳播(propagation)[20]的概念來幫助解決這種狀況。咱們正在採用傳播的方式來實現頭的定製的通常模型。
關於更普遍的概念背景:
System.Diagnostics.Activity
表示。下面的代碼演示了使用傳播的通常方法:
DistributedContextPropagator propagator = DistributedContextPropagator.Current; propagator.Inject(activity, carrier, (object theCarrier, string fieldName, string value) => { // Extract the context from the activity then inject it to the carrier. });
你也能夠選擇使用不一樣的傳播器(propagator):
// Set the current propagation behavior to not transmit any distributed context information in outbound network messages. DistributedContextPropagator.Current = DistributedContextPropagator.CreateNoOutputPropagator();
DistributedContextPropagator
抽象類決定了分佈式上下文信息在網絡傳輸時是否以及如何被編碼和解碼。編碼能夠經過任何支持字符串鍵/值對的網絡協議進行傳輸。DistributedContextPropagator
以字符串鍵/值對的形式向載體注入數值並從載體中提取數值。經過添加對傳播者的支持,咱們實現了兩件事。
HttpClient
、ASP.NET Core 等庫對這種自定義格式有預先的瞭解。Dictionary<string, string>
),你如今能夠支持各類格式。大多數應用程序代碼不須要直接使用這個功能,然而,若是你使用 OpenTelemetry
,你極可能會在調用棧中看到它。一些庫的代碼若是關心跟蹤和因果關係,可能會須要使用這個模型。
.NET 的加密和解密部件是圍繞着流設計的,沒有真正的概念來定義何時有效載荷已經在內存中(already in memory)。SymmetricAlgorithm
上新的 Encrypt-
和 Decrypt-
方法加速了 already in memory 的進展,目的是爲調用者和代碼審查者提供清晰的信息。此外,它們還支持從 span 中讀取和寫入。
新的簡化方法爲使用加密 API 提供了一個直接的方法:
private static byte[] Decrypt(byte[] key, byte[] iv, byte[] ciphertext) { using (Aes aes = Aes.Create()) { aes.Key = key; return aes.DecryptCbc(ciphertext, iv); } }
在新的 Encrypt-
和 Decrypt-
方法中,只使用 SymmetricAlgorithm
實例中的 Key
屬性。新的 DecryptCbc
方法支持選擇填充算法,可是 PKCS#7 常常與 CBC 一塊兒使用,因此它是一個默認參數。若是你喜歡這種清晰的感受,就指定它吧:
private static byte[] Decrypt(byte[] key, byte[] iv, byte[] ciphertext) { using (Aes aes = Aes.Create()) { aes.Key = key; return aes.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7); } }
你能夠看到,現有的模式--使用 .NET 5--明顯須要更多的管道來實現一樣的結果:
private static byte[] Decrypt(byte[] key, byte[] iv, byte[] ciphertext) { using (Aes aes = Aes.Create()) { aes.Key = key; aes.IV = iv; // These are the defaults, but let's set them anyways. aes.Padding = PaddingMode.PKCS7; aes.Mode = CipherMode.CBC; using (MemoryStream destination = new MemoryStream()) using (ICryptoTransform transform = aes.CreateDecryptor()) using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write)) { cryptoStream.Write(ciphertext, 0, ciphertext.Length); cryptoStream.FlushFinalBlock(); return destination.ToArray(); } } }
運行時如今有一種模式,它不建立或使用任何同時可寫和可執行的內存頁。全部可執行的內存都被映射爲只讀不執行。這項功能在該版本的早期僅在 macOS 上啓用--針對 Apple Silicon。在 Apple Silicon 機器上,禁止同時進行可寫和可執行的內存映射。
這一功能如今在全部其餘平臺上被啓用並支持。在這些平臺上,可執行代碼的生成/修改是經過單獨的讀寫內存映射完成的,這對 JIT 代碼和運行時生成的輔助程序都是如此。這些映射是在與可執行代碼地址不一樣的虛擬內存地址上建立的,而且只在進行寫入時存在很是短暫的時間。例如,JIT 如今生成代碼到一個從頭開始的緩衝區,在整個方法被 jitted 後,使用一個內存拷貝函數調用將其複製到可執行內存中。而可寫映射的壽命只跨越了內存拷貝的時間。
這個新功能能夠經過設置環境變量 DOTNET_EnableWriteXorExecute
爲 1 來啓用。這個功能在 .NET 6 中是可選的,由於它有一個啓動時的退步(除了在 Apple Silicon 上)。在咱們的 ASP.NET 基準測試中,當用 Ready To Run(R2R)編譯時,退步了 ~10%。然而,在啓用和未啓用該功能的狀況下,測得的穩態性能是同樣的。對於啓動性能並不重要的應用程序,咱們建議啓用該功能,由於它能提升安全性。咱們打算做爲 .NET 7 的一部分解決性能退步問題,屆時默認啓用該功能。
咱們認爲,咱們已經到了新功能和改進已經完成的發佈點。爲團隊點贊!
咱們繼續期待大家的反饋。咱們將把 .NET 6 的其他部分放在完善(功能和性能)和新功能中發現的錯誤上。在大多數狀況下,功能改進須要等到 .NET 7。請分享你的任何反饋,咱們將很高興對其進行分類。
感謝全部爲 .NET 6 作出貢獻的人,使其成爲另外一個偉大的版本。
感謝你成爲一名 .NET 開發者。
文中相關連接:
[1].https://dotnet.microsoft.com/ [2].https://dotnet.microsoft.com/download/dotnet/6.0 [3].https://devblogs.microsoft.com/dotnet/announcing-net-maui-preview-7/ [4].https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-7/ [5].https://devblogs.microsoft.com/dotnet/category/conversations/ [6].https://devblogs.microsoft.com/dotnet/string-interpolation-in-c-10-and-net-6/ [7].https://docs.microsoft.com/dotnet/core/compatibility/sdk/6.0/csharp-template-code [8].https://docs.microsoft.com/dotnet/core/compatibility/sdk/6.0/implicit-namespaces [9].https://docs.microsoft.com/dotnet/csharp/nullable-references [10].https://github.com/dotnet/runtime/issues/29723 [11].https://twitter.com/JeffHandley/status/1424846146850131968 [12].https://github.com/jellyfin/jellyfin/blob/c07e83fdf87e61f30e4cca4e458113ac315918ae/Directory.Build.props#L5 [13].https://github.com/dotnet/roslyn/blob/main/docs/features/nullable-metadata.md [14].https://github.com/dotnet/designs/blob/main/accepted/2021/preview-features/preview-features.md [15].https://devblogs.microsoft.com/dotnet/preview-features-in-net-6-generic-math/ [16].https://github.com/dotnet/runtime/pull/54006 [17].https://github.com/dotnet/runtime/issues/1574 [18].https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-source-generator/ [19].https://devblogs.microsoft.com/dotnet/opentelemetry-net-reaches-v1-0/ [20].https://opentelemetry.lightstep.com/core-concepts/context-propagation/