被微軟形容為“迄今為止最高效、最現代、最安全、最智慧、效能最高的.NET版本”——.NET 9已經發布有一週了,今天想和大家一起體驗一下新功能。
此次.NET 9在效能、安全性和功能等方面進行了大量改進,包含了數千項的修改,今天主要和大家一起體驗我們常用有變化的功能。
01、安裝
首先可以在命令列中透過dotnet --list-sdks指令檢視是否以安裝.NET9。
安裝方式主要有兩種,第一種透過直接下載.NET9 SDK安裝。
第二種方式透過更新IDE Visual Studio至17.12.0或之後的版本,比如我的IDE是17.11.6,選擇更新即可,如果是更老的版本,比如17.11.4則需要更新兩次才行。
再次執行dotnet --list-sdks命令,發現已安裝成功,結果如下:
02、新的轉義序列
本次C#13新引入了 \e 作為 ESC (Escape)字元 Unicode U+001B 的字元文字轉義序列。而以前使用的是 \u001b 或 \x1b。
ESC (Escape) 字元(ASCII 編碼值為 27,十六進位制為 0x1b)是一種控制字元,可以用於 終端控制和文字格式化。它本身代是不可見字元,主要作用標誌著後續字元序列的開始,這些序列會指定終端執行特定的操作,如改變文字顏色、設定文字樣式、清屏、移動游標等。
我們舉一個例子,比如“ESC[31m ”表示把文字顏色設定為紅色,我們把ESC改為轉義序列,程式碼如下:
static void Main(string[] args)
{
var colorRed = "\x1b[31m紅色文字";
Console.WriteLine(colorRed);
Console.ReadKey();
}
執行效果如下:
如果單獨使用ESC (Escape)字元的轉義序列則表示其後接著的字元不可見,我們可以用\u001b和\x1b看看效果,結果如下,發現看字沒有展示出來:
然後我們把\u001b和\x1b改為\u001b1和\x1b1,再看一下效果:
從上圖可以發現使用\u001b1結果還是\u001b後面一個字元看不見,而使用\x1b1後變成問號“?”了,這是因為\x1b1整體被識別為一個更長的十六進位制序列,而不是單純的控制字元了,這就產生了歧義,因此不建議使用\x1b,因而這次新增了跳脫字元\e,效果如下。
03、隱式索引訪問
現在物件初始值設定項表示式中允許隱式“從末尾開始”索引運算子[^]。
我們來看一個例子,首先建立了一個ImplicitIndex類並且包含一個Numbers屬性,該屬性是一個長度為 5 的整數陣列。
現在我們可以在初始化類ImplicitIndex時初始化屬性Numbers,並使用“從末尾開始”索引運算子來填充陣列值。
我們先看看在.NET8中的效果,滑鼠移到錯誤上可以看到老版本是不支援該功能的,如下圖:
而使用.NET9則可以。
04、params引數增強
之前params只支援陣列,而現在params引數支援更多的集合型別,包括 Span
public static void PrintNumbers(params int[] numbers) { }
public static void PrintNumbers(params List<string> numbers) { }
public static void PrintNumbers(params HashSet<int> numbers) { }
public static void PrintNumbers(params SortedSet<int> numbers) { }
public static void PrintNumbers(params IList<int> numbers) { }
public static void PrintNumbers(params ICollection<int> numbers) { }
public static void PrintNumbers(params IEnumerable<int> numbers) { }
public static void PrintNumbers(params Span<int> numbers) { }
public static void PrintNumbers(params ReadOnlySpan<int> numbers) { }
params 關鍵字允許方法接收一個可變數量的引數,通常是單一型別的引數集合。params 的引數通常可以是陣列、集合或其他實現了 IEnumerable
public static void PrintNumbers(params Dictionary<int, int> numbers) { }
public static void PrintNumbers(params SortedList<int, int> numbers) { }
public static void PrintNumbers(params LinkedList<int> numbers) { }
public static void PrintNumbers(params Queue<int> numbers) { }
public static void PrintNumbers(params Queue numbers) { }
public static void PrintNumbers(params Stack<int> numbers) { }
public static void PrintNumbers(params Stack numbers) { }
public static void PrintNumbers(params Hashtable numbers) { }
05、鎖物件
本次更新引入新的鎖型別System.Threading.Lock,用於實現互斥。在之前的版本中通常透過object型別進行加鎖,而現在有了專門的Lock型別用來加鎖。
新的Lock型別會使得程式碼更乾淨、更安全、更高效。
在新的鎖定機制中EnterScope替換了Monitor底層實現。同時它遵循Dispose模式返回ref struct,因此可以與using語句結合使用。
我們一起看看下面程式碼示例:
// .NET 9 之前
public class LockExampleNET9Before
{
private readonly object _lock = new();
public void Print()
{
lock (_lock)
{
Console.WriteLine("我們是老的鎖");
}
}
}
// .NET 9
public class LockExampleNET9
{
private readonly Lock _lock = new();
public void Print()
{
lock (_lock)
{
Console.WriteLine("我們是 .NET 9 新鎖");
}
}
public async Task LogMessageAsync(string message)
{
using (_lock.EnterScope())
{
Console.WriteLine("我們是 .NET 9 新鎖,可以和using一起使用");
}
}
}
06、生成UUID v7
我們經常在實體中使用Guid作為主鍵,並且透過Guid.NewGuid()可以很方便的生成一個新的Guid,而此方法生成的Guid是依據UUID第四個版本規範生成的。
當前已經可以透過Guid.CreateVersion7()方法建立UUID第七個版本,這個版本UUID主要功能就是包含了時間戳,資料結構如下:
| 48位時間戳 | 12位隨機 | 62位隨機 |
這也意味著v7版本的UUID可以按時間排序了,在資料庫中使用起來更方便,同時Guid.CreateVersion7()方法還有一個過載方法接收DateTimeOffset型別時間戳,用來透過指定時間建立UUID。
static void Main()
{
// v4 UUID
var guid_v4 = Guid.NewGuid();
// v7 UUID
var guid_v7 = Guid.CreateVersion7();
// v7 UUID with timestamp
var guid_v7_time = Guid.CreateVersion7(TimeProvider.System.GetLocalNow());
Console.ReadKey();
}
注:測試方法程式碼以及示例原始碼都已經上傳至程式碼庫,有興趣的可以看看。https://gitee.com/hugogoos/Planner