C# 9.0 特性全面總結(jié)
頂級語句
頂級語句可以刪除程序中不必要的代碼, 以最簡單的 Hello, world! 為例:
using System; namespace HelloWorld { class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } } }
如果使用頂級語句的話, 可以簡化為:
using System; Console.WriteLine("Hello World!");
如果不使用 using , 還可以更加簡化:
System.Console.WriteLine("Hello World!");
頂級語句在很多命令行程序、小工具程序中會非常有用, 對應(yīng)用程序的作用域或者復(fù)雜程度沒有任何限制。
注意, 一個程序中, 只能有一個文件使用頂級語句, 并且頂級語句必須位于命名空間或類型定義之前!
棄元參數(shù)
在 lambda 表達式或者匿名函數(shù)中如果要忽略某個參數(shù), 可以用 _ 代替。
var button = new Button("Click Me!"); button.Click += (_, e) => { /* other code goes here. */ };
僅初始化設(shè)置器 (Init only setters)
創(chuàng)建只能通過對象初始化進行賦值的屬性。
public class InitDemo { public string Start { get; init; } public string Stop { get; init; } } // initDemo.Start = "Now"; // Error // initDemo.End = "Tomorrow"; // Error var initDemo = new InitDemo { Start = "Now", Stop = "Tomorrow" };
記錄類型 (Record)
記錄類型, 是一種引用類型, 默認是不可變的。 記錄類型的相等判斷可以通過引用或者結(jié)構(gòu)進行判斷的。
// 默認不可變的記錄類型 public record Person(string Name, int Age); // 可變記錄類型 public record MutablePerson(string Name, int Age) { public string Name { get; set; } = Name; public int Age { get; set; } = Age; } var person1 = new Person("Zhimin Zhang", 40); var person2 = new Person("Zhimin Zhang", 40); Console.WriteLine(person1 == person2); // True 結(jié)構(gòu)相同 Console.WriteLine(person1.Equals(person2)); // True 結(jié)構(gòu)相同 Console.WriteLine(ReferenceEquals(person1, person2)); // False, 引用不同 // 改變默認的記錄! --> 創(chuàng)建一個新的記錄。 var person3 = person1 with { Age = 43 }; Console.WriteLine(person3 == person1); // False 結(jié)構(gòu)不同 // 解構(gòu) (Destruct) 一個記錄, 將記錄的屬性提取為本地變量 var (name, age) = person3; var person4 = new MutablePerson("Zhimin zhang", 40); person4.Age = 43; var person5 = new Citizen("Zhimin Zhang", 40, "China"); Console.WriteLine(person1 == person5); // 記錄類型也可以被繼承 public record Citizen(string Name, int Age, string Country) : Person(Name, Age); var citizen = new Citizen("Zhimin Zhang", 40, "China"); Console.WriteLine(person1 == citizen); // False 類型不同;
- 優(yōu)點:記錄類型是輕量級的不可變類型,可以減少大量的代碼, 可以按照結(jié)構(gòu)和引用進行比較;
- 缺點:需要實例化大量的對象;
如果要更加深入的學(xué)習記錄類型, 請查看微軟的官方文檔 exploration of records 。
模式匹配增強
C# 9 包含了一些新的模式匹配增強:
Type patterns 類型匹配,判斷一個變量的類型
object obj = new int(); var type = obj switch { string => "string", int => "int", _ => "obj" }; Console.WriteLine(type); // int
Relational patterns 關(guān)系匹配
// Relational patterns var person1 = new Person("Zhimin Zhang", 40); var inRange = person1 switch { (_, < 18) => "less than 18", (_, > 18) => "greater than 18", (_, 18) => "18 years old!" }; Console.WriteLine(inRange); // greater than 18
Conjunctive and patterns 邏輯與匹配
// And pattern var person1 = new Person("Zhimin Zhang", 40); var ageInRange = person1 switch { (_, < 18) => "less than 18", ("Zhang Zhimin", _) and (_, >= 18) => "Zhimin Zhang is greater than 18" }; Console.WriteLine(ageInRange); // Zhimin Zhang is greater than 18
Disjunctive or patterns 邏輯或匹配
// Or pattern var person1 = new Person("Zhimin Zhang", 40); var ageInRange = person1 switch { (_, < 18) => "less than 18", (_, 18) or (_, > 18) => "18 or greater" }; Console.WriteLine(ageInRange); // 18 or greater
Negated not patterns 邏輯非匹配
// Not pattern var person1 = new Person("Zhimin Zhang", 40); var meOrNot = person1 switch { not ("Zhimin Zhang", 40) => "Not me!", _ => "Me :-)" }; Console.WriteLine(meOrNot); // Me :-)
Parenthesized patterns 帶括號的優(yōu)先級匹配
// Parenthesized patterns var is10 = new IsNumber(true, 10); var n10 = is10 switch { ((_, > 1 and < 5) and (_, > 5 and < 9)) or (_, 10) => "10", _ => "not 10" }; Console.WriteLine(n10); // 10
注意, 如果沒有匹配到全部的情況, 將會出現(xiàn)異常。
新的初始化表達式
在C#9.0中,當已創(chuàng)建對象的類型已知時,可以在new表達式中省略該類型。
public class MyClass { private List<WeatherObservation> _observations = new(); } Point p = new(1, 1); Dictionary<string, int> dict = new(); Point[] points = { new(1, 1), new (2, 2), new (3, 3) }; var list = new List<Point> { new(1, 1), new(2, 2), new(3, 3) };
- 優(yōu)點: 可以讓代碼更加簡潔;
- 缺點: 某些情況下會讓代碼更難理解;
目標類型條件表達式
可以隱式轉(zhuǎn)換 null 值, 在 C#9.0 中得到了增強。
void TestMethod(int[] list, uint? u) { int[] x = list ?? new int[0]; var l = u ?? -1u; }
GetEnumerator 擴展
可以為任意類型添加一個 GetEnumerator<T> 擴展, 返回一個 IEnumerator<T> 或者 IAsyncEnumerator<T> 實例, 從而在 foreach 循環(huán)中使用。
public static class Extensions { public static IEnumerator<T> GetEnumerator<T>(this IEnumerator<T> enumerator) => enumerator; } IEnumerator<string> enumerator = new Collection<string> { "A", "B", "C" }.GetEnumerator(); foreach (var item in enumerator) { Console.WriteLine(item); }
在本地函數(shù)上添加標記
允許在本地函數(shù)上添加標記。
static void Main(string[] args) { [Conditional("DEBUG")] static void DoSomething([NotNull] string test) { Console.WriteLine("Do it!"); } DoSomething("Doing!"); }
分部方法擴展
在C#9.0中,移除了分部方法的幾個限制:
- 必須具有 void 返回類型。
- 不能具有 out 參數(shù)。
- 不能具有任何可訪問性(隱式 private )。
partial class Doing { internal partial bool DoSomething(string s, out int i); } partial class Doing { internal partial bool DoSomething(string s, out int i) { i = 0; return true; } }
靜態(tài) lambda 表達式
從 C#9.0 開始,可以將 static 修飾符添加到 lambda 表達式或 匿名方法 。靜態(tài) lambda 表達式類似于 static 局部函數(shù):靜態(tài)lambda或匿名方法無法捕獲局部變量或?qū)嵗隣顟B(tài)。 所述 static 可以防止意外捕獲其他變量。
lambda 表達式會捕獲上下文的變量,不僅會有性能的問題,而且還可能出現(xiàn)錯誤,比如:
int number = 0; Func<string> toString = () => number.ToString(); // number 被自動捕獲進 toString 函數(shù)中
可以在 lambda 表達式前添加 static 關(guān)鍵字, 來解決這個問題:
int number = 0; Func<string> toString = static () => number.ToString(); // 這里無法再使用 number ;
模塊初始化代碼
可以使用 ModuleInitializerAttribute 為組件 (assembly) 定義初始化代碼, 當初始化/加載時執(zhí)行, 可以類比類的靜態(tài)構(gòu)造函數(shù), 但是是組件級別的, 要求如下:
- 必須是靜態(tài)的、無參數(shù)的、無返回值的方法;
- 不能是范型方法,也不能包含在范型類中;
- 不能是私有函數(shù),必須是公開 (public) 或者內(nèi)部 (internal) 的函數(shù);
協(xié)變返回類型
協(xié)變返回類型為重寫方法的返回類型提供了靈活性。覆蓋方法可以返回從覆蓋的基礎(chǔ)方法的返回類型派生的類型。這對于記錄和其他支持虛擬克隆或工廠方法的類型很有用。 比如:
public virtual Person GetPerson() { return new Person(); } public override Person GetPerson() { return new Student(); }
在 C# 9.0 中, 可以在子類中返回更加詳細的類型:
public virtual Person GetPerson() { return new Person(); } public Student Person GetPerson() { return new Student(); }
原生整數(shù)類型
C#9 添加了兩個新的整數(shù)類型 (nint 和 nunit) , 依賴宿主機以及編譯設(shè)定。
nint nativeInt = 55; Console.WriteLine(nint.MaxValue); // 在 x86 平臺上, 輸出為 2147483647 // 在 x64 平臺上, 輸出為 9223372036854775807
- 優(yōu)點:可以更好的兼容原生API;
- 缺點:缺失平臺無關(guān)性;
跳過本地初始化 (SkipLocalInit)
在 C#9.0 中,可以使用 SkipLocalsInitAttribute 來告知編譯器不要發(fā)射 (Emit) .locals init 標記。
[System.Runtime.CompilerServices.SkipLocalsInit] static unsafe void DemoLocalsInit() { int x; // 注意, x 沒有初始化, 輸出結(jié)果不確定; Console.WriteLine(*&x); }
- 優(yōu)點:跳過本地初始化可以提升程序的性能;
- 缺點:性能的影響通常不大,建議只在極端情況下才使用這個;
函數(shù)指針
使用 delegate* 可以聲明函數(shù)指針。
unsafe class FunctionPointer { static int GetLength(string s) => s.Length; delegate*<string, int> functionPointer = &GetLength; } public void Test() { Console.WriteLine(functionPointer("test")); // 4; }
以上就是C# 9.0 特性全面總結(jié)的詳細內(nèi)容,更多關(guān)于C# 9.0 特性的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#實現(xiàn)二維數(shù)據(jù)數(shù)組導(dǎo)出到Excel的詳細過程
將數(shù)據(jù)庫查詢出來的數(shù)據(jù)導(dǎo)出并生成?Excel?文件,是項目中經(jīng)常使用的一項功能,本文將介紹通過數(shù)據(jù)集生成二維數(shù)據(jù)數(shù)組并導(dǎo)出到?Excel,文中有詳細的代碼供大家參考,需要的朋友可以參考下2024-09-09C#實現(xiàn)將RTF轉(zhuǎn)為HTML的示例代碼
RTF文檔即富文本格式(Rich?Text?Format)的文檔。我們在處理文件時,遇到需要對文檔格式進行轉(zhuǎn)換時,可以將RTF轉(zhuǎn)為其他格式,如轉(zhuǎn)為DOCX/DOC、PDF或者HTML。本文將利用C#實現(xiàn)RTF轉(zhuǎn)HTML,需要的可以參考一下2022-04-04