在我們還在為 .NET 7 興奮的時候,.NET 8 已經來臨了哦!時間過得真快,當你熱衷於編碼時,這個第一個預覽和.NET 8預覽版已經釋出了。在寫此文章時,已推出到最新的預覽版是5.0
但不用擔心,微軟正努力為我們帶來最新和最棒的功能,我們將在本文中一一探索這些功能。
接下來,就讓我們一起來看看.NET 8為我們帶來了哪些令人興奮的新更新。
微軟剛剛為 dotnet publish
和 dotnet pack
命令推出了一個很棒的新功能,這使得生成適用於生產的代碼變得更加容易。
在此更新之前,這些命令默認生成 Debug
版本的代碼,如果你想生成適用於生產的代碼,可能有點麻煩。但是現在,通過這個新的更新,dotnet publish
和 dotnet pack
默認生成 Release
版本的代碼,這意味著你可以輕鬆生成適用於生產的代碼,無需任何額外的步驟。
但是不用擔心,如果你仍然需要出於任何原因生成 Debug
版本的代碼,可以通過將 PublishRelease
屬性設置為 false
來實現。
首先,讓我們使用 dotnet new console
命令創建一個新的控制台應用程序。然後,使用 dotnet build
命令來構建項目並查看輸出。在這種情況下,輸出將處於 Debug
模式,因為這是 dotnet build
的默認行為。
接下來,讓我們運行 dotnet publish
命令來生成適用於生產的代碼。通過這個新的更新,這個命令將默認生成 Release
版本的代碼,這正是我們想要的適用於生產的代碼。我們可以在 /app/bin/Release/net8.0
目錄中看到 Release
版本的代碼。
最後,假設我們需要出於某些原因生成 Debug
版本的代碼。我們可以再次運行 dotnet publish
命令,但這次我們將將 PublishRelease
屬性設置為 false
。這將生成 Debug
版本的代碼,我們可以在 /app/bin/Debug/net8.0
目錄中看到。
就是這樣!有了這個新功能,使用 dotnet publish
和 dotnet pack
命令生成適用於生產的代碼變得比以往更加容易。
📚參考資料:dotnet publish 和 dotnet pack
在這個 .NET 8 預覽1 中,System.Text.Json
是一個內建的 .NET 库,提供了 JSON 序列化和反序列化功能。它允許開發人員將 .NET 對象轉換為 JSON 數據,反之亦然。
現在,System.Text.Json
的序列化和反序列化功能在 .NET 8 中得到了各種方面的改進。
這個預覽版本中包含的另一系列改進是在與 ASP.NET Core 在原生 AOT 應用程序中使用時的源代碼生成器,使其更可靠和更快。
此外,源代碼生成器現在將支持在 .NET 8 中序列化帶有 required
和 init
屬性的類型,這在基於反射的序列化中已經支持。
此外,現在可以自定義在 JSON 數據中不存在的成員的序列化。
最後,接口層次結構中的屬性現在可以序列化,包括立即實現的接口和基本接口的屬性。讓我們看看這個例子:
IDerived value = new DerivedImplement { Base = 0, Derived =1 };
JsonSerializer.Serialize(value); // {"Base":0,"Derived":1}
public interface IBase
{
public int Base { get; set; }
}
public interface IDerived : IBase
{
public int Derived { get; set; }
}
public class DerivedImplement : IDerived
{
public int Base { get; set; }
public int Derived { get; set; }
}
現在,JsonNamingPolicy 已擴展,可以包括將屬性名轉換為 snake_case
(使用下劃線)和 kebab-case(使用連字符)的命名策略。這些新策略可以像使用 JsonNamingPolicy.CamelCase
策略一樣使用。
var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower };
JsonSerializer.Serialize(new { PropertyName = "value" }, options); // { "property_name" : "value" }
JsonSerializerOptions.MakeReadOnly()
方法允許你明確控制何時凍結 JsonSerializerOptions
實例。你也可以使用 IsReadOnly
屬性檢查它是否為只讀。
GetItems<T>() 方法是 .NET 8 中的一個新功能,它允許你從給定的元素集合中隨機選擇特定數量的項目。
這在遊戲、模擬和其他需要隨機性的應用程序中很有用。該方法在 System.Random
和 System.Security.Cryptography.RandomNumberGenerator
中都可用。
在這個例子中,我們有一個 City
對象的數組,我們使用 GetItems<T>()
方法從數組中隨機選擇 3 個城市:
private static ReadOnlySpan<City> s_allCities = new[]
{
new City("New York", "USA"),
new City("London", "UK"),
new City("Paris", "France"),
new City("Tokyo", "Japan"),
new City("Sydney", "Australia"),
};
...
City[] selectedCities = Random.Shared.GetItems(s_allCities, 3);
foreach (City city in selectedCities)
{
Console.WriteLine(city.Name + ", " + city.Country);
}
// Output:
// Paris, France
// Tokyo, Japan
// Sydney, Australia
然後,我們遍歷所選的城市,將它們的名稱和國家打印到控制台上。每次運行程序時,輸出將不同,因為城市是隨機選擇的。
📚參考資料:GetItems<T>()
Shuffle<T>() 方法是 .NET 8 中的另一個新功能,它允許你隨機打亂一個 span
中元素的順序。
這在機器學習實例中很重要,當你希望通過隨機化訓練數據順序來消除訓練偏差時。
下面是一個示例,展示如何使用 Shuffle<T>()
方法與 YourType
對象的數組:
YourType[] trainingData = LoadTrainingData();
Random.Shared.Shuffle(trainingData);
IDataView sourceData = mlContext.Data.LoadFromEnumerable(trainingData);
DataOperationsCatalog.TrainTestData split = mlContext.Data.TrainTestSplit(sourceData);
model = chain.Fit(split.TrainSet);
IDataView predictions = model.Transform(split.TestSet);
// ...
在這個例子中,我們將一些訓練數據加載到 YourType
對象的數組中,並使用 Random.Shared
將元素的順序打亂。
然後,我們將打亂的數據加載到 IDataView
對象中,將數據分成訓練集和測試集,並使用打亂的訓練數據來訓練機器學習模型。最後,我們使用訓練好的模型對測試集進行預測。
📚參考資料:Shuffle<T>()
.NET 8 引入了幾種新的類型,旨在提高應用程序的性能。這些新類型包括:
System.Collections.Frozen
命名空間包括兩種集合類型,FrozenDictionary<TKey,TValue>
和 FrozenSet<T>
。這些類型在創建集合後不允許對鍵和值進行任何更改,這使得像 TryGetValue()
這樣的讀操作更快。它們對於在首次使用時填充集合然後在長期運行的服務期間保留集合非常有用:private static readonly FrozenDictionary<string, bool> s_configurationData =
LoadConfigurationData().ToFrozenDictionary(optimizeForReads: true);
// ...
if (s_configurationData.TryGetValue(key, out bool setting) && setting)
{
Process();
}
System.Text.CompositeFormat
類型對於優化在編譯時未知的格式字符串非常有用。一開始需要花費一些額外時間進行解析等工作,但可以避免每次使用時都進行這些工作:private static readonly CompositeFormat s_rangeMessage = CompositeFormat.Parse(LoadRangeMessageResource());
// ...
static string GetMessage(int min, int max) =>
string.Format(CultureInfo.InvariantCulture, s_rangeMessage, min, max);
System.Buffers.IndexOfAnyValues<T>
類型設計用於傳遞給尋找傳入集合中任何值的方法。.NET 8 添加了對 String.IndexOfAny
和 MemoryExtensions.IndexOfAny
等方法的新重載,這些方法接受新類型的實例。📚參考資料:性能專注型類型
.NET 8 對原生預先編譯(AOT)功能進行了改進,這一功能首次在 .NET 7 中引入。將應用程序發布為原生 AOT 會生成一個自包含版本的應用程序,不需要運行時,因為所有內容都包含在一個單獨的文件中。
除了對各種平台的現有支持外,.NET 8 現在還包括對 macOS 上 x64 和 Arm64 架構的支持。這意味著開發人員現在可以將他們的 .NET 應用程序發布為針對 macOS 系統的原生 AOT。
對於 Linux 系統上的原生 AOT 應用程序的最新改進使應用程序的大小顯著減小。
根據最近的測試,使用 .NET 8 Preview 1 構建的原生 AOT 應用程序的大小比使用 .NET 7 構建的應用程序減少了多達 50%。
您可以在下表中看到使用原生 AOT 發布並包含整個 .NET 運行時的 "Hello World"
應用程序大小在兩個版本之間的比較:
操作系統 | .NET 7 | .NET 8 預覽版 |
---|---|---|
Linux x64 (with -p:StripSymbols=true) | 3.76 MB | 1.84 MB |
Windows x64 | 2.85 MB | 1.77 MB |
這些原生 AOT 的改進有助於 .NET 開發人員創建更小、更快、更高效的應用程序,無需任何外部依賴即可在各種平台上運行。
📚參考資料:原生 AOT
.NET 8 在程式碼生成和 JIT(即時編譯)編譯方面也進行了一些改進,增強了性能和效率:
Arm64 架構的性能改進
SIMD(單指令多數據)改進,以實現更好的向量化和操作的並行處理
針對容器化環境的雲原生改進,以實現更好的性能
基於應用程序使用模式的剖面引導優化(PGO)改進,實現更好的優化
支援 AVX-512 ISA 擴展,以實現現代 CPU 上更高效的浮點操作
JIT(即時編譯)吞吐量改進,實現更快的程式碼生成
循環和通用優化,改善經常使用的程式碼塊的性能
這些改進有助於開發人員優化其 .NET 應用程序的性能,並減少在雲原生環境中的資源利用。
📚參考資料:程式碼生成
.NET 8 也對 .NET 容器映像的工作方式進行了一些更改。首先,Debian 12(Bookworm)現在是容器映像中的默認 Linux 發行版。
此外,這些映像包含一個非根用戶,使映像能夠運行在非根能力的環境中。要以非根身份運行,只需在 Dockerfile 的末尾添加 USER app 這一行,或在 Kubernetes 配置文件中添加類似的指令。
默認端口也從 80 更改為 8080,並提供一個新的環境變量 ASPNETCORE_HTTP_PORTS
,使更容易更改端口。
ASPNETCORE_HTTP_PORTS
變量的格式與 ASPNETCORE_URLS
所需的格式相比更簡單,並且可以接受端口列表。如果使用這些變量將端口改回 80,將無法以非根身份運行。
要拉取 .NET 8 預覽版 SDK,可以使用以下標籤,該標籤名稱包含預覽容器映像的 -preview
後綴:
docker run --rm -it mcr.microsoft.com/dotnet/sdk:8.0-preview
在發行候選版本(RC)中,將不再使用 -preview 後綴。此外,開發人員可以使用具有 .NET 8 的切削 Ubuntu 映像,這些映像具有較小的攻擊面,沒有套件管理器或 shell,並且具有非根能力。這些映像非常適合尋求應用程式式計算優勢的開發人員。
📚參考資料:.NET 容器映像
那麼,你現在對於在 .NET 8 中嘗試這些新功能感到興奮嗎?你認為哪個新增功能對您的項目最有用呢?請毫不猶豫地下載預覽版並探索其中的可能性。祝您編碼愉快!:laughing: