C#12中的新增功能使用總結(jié)
新的 C# 12 功能在預(yù)覽版中已經(jīng)引入. 您可以使用最新的 Visual Studio 預(yù)覽版或最新的 .NET 8 預(yù)覽版 SDK 來嘗試這些功能。以下是一些新引入的功能:
- 主構(gòu)造函數(shù)
- 集合表達式
- 默認 Lambda 參數(shù)
- 任何類型的別名
- 內(nèi)聯(lián)數(shù)組
- 攔截器
- 使用nameof訪問實例成員
主構(gòu)造函數(shù)
現(xiàn)在可以在任何 class 和 struct 中創(chuàng)建主構(gòu)造函數(shù)。 主構(gòu)造函數(shù)不再局限于 record 類型。 主構(gòu)造函數(shù)參數(shù)都在類的整個主體的范圍內(nèi)。 為了確保顯式分配所有主構(gòu)造函數(shù)參數(shù),所有顯式聲明的構(gòu)造函數(shù)都必須使用 this() 語法調(diào)用主構(gòu)造函數(shù)。 將主構(gòu)造函數(shù)添加到 class 可防止編譯器聲明隱式無參數(shù)構(gòu)造函數(shù)。 在 struct 中,隱式無參數(shù)構(gòu)造函數(shù)初始化所有字段,包括 0 位模式的主構(gòu)造函數(shù)參數(shù)。
編譯器僅在 record 類型(record class 或 record struct 類型)中為主構(gòu)造函數(shù)參數(shù)生成公共屬性。 對于主構(gòu)造函數(shù)參數(shù),非記錄類和結(jié)構(gòu)可能并不總是需要此行為。
主構(gòu)造函數(shù)的參數(shù)位于聲明類型的整個主體中。 它們可以初始化屬性或字段。 它們可用作方法或局部函數(shù)中的變量。 它們可以傳遞給基本構(gòu)造函數(shù)。
主構(gòu)造函數(shù)指示這些參數(shù)對于類型的任何實例是必需的。 任何顯式編寫的構(gòu)造函數(shù)都必須使用 this(...) 初始化表達式語法來調(diào)用主構(gòu)造函數(shù)。 這可確保主構(gòu)造函數(shù)參數(shù)絕對由所有構(gòu)造函數(shù)分配。 對于任何 class 類型(包括 record class 類型),當主構(gòu)造函數(shù)存在時,不會發(fā)出隱式無參數(shù)構(gòu)造函數(shù)。 對于任何 struct 類型(包括 record struct 類型),始終發(fā)出隱式無參數(shù)構(gòu)造函數(shù),并始終將所有字段(包括主構(gòu)造函數(shù)參數(shù))初始化為 0 位模式。 如果編寫顯式無參數(shù)構(gòu)造函數(shù),則必須調(diào)用主構(gòu)造函數(shù)。 在這種情況下,可以為主構(gòu)造函數(shù)參數(shù)指定不同的值。
下面看下主構(gòu)造函數(shù)的應(yīng)用場景
初始化屬性
以下代碼初始化從主構(gòu)造函數(shù)參數(shù)計算的兩個只讀屬性:
public readonly struct Distance(double dx, double dy) { public readonly double Magnitude = Math.Sqrt(dx * dx + dy * dy); public readonly double Direction = Math.Atan2(dy, dx); }
前面的代碼演示了用于初始化計算的只讀屬性的主構(gòu)造函數(shù)。 Direction 和 Magnitude 的字段初始值設(shè)定項使用主構(gòu)造函數(shù)參數(shù)。 主構(gòu)造函數(shù)參數(shù)不會在結(jié)構(gòu)中的其他任何位置使用。 前面的結(jié)構(gòu)就像編寫了以下代碼一樣:
public readonly struct Distance { public readonly double Magnitude { get; } public readonly double Direction { get; } public Distance(double dx, double dy) { Magnitude = Math.Sqrt(dx * dx + dy * dy); Direction = Math.Atan2(dy, dx); } }
當需要參數(shù)來初始化字段或?qū)傩詴r,利用新功能可以更輕松地使用字段初始值設(shè)定項。
創(chuàng)建可變狀態(tài)
前面的示例使用主構(gòu)造函數(shù)參數(shù)來初始化只讀屬性。 如果屬性不是只讀的,你還可以使用主構(gòu)造函數(shù)。 考慮下列代碼:
public struct Distance(double dx, double dy) { public readonly double Magnitude => Math.Sqrt(dx * dx + dy * dy); public readonly double Direction => Math.Atan2(dy, dx); public void Translate(double deltaX, double deltaY) { dx += deltaX; dy += deltaY; } public Distance() : this(0,0) { } }
在前面的示例中,Translate 方法了更改 dx 和 dy 組件。 這就需要在訪問時計算 Magnitude 和 Direction 屬性。 => 運算符指定一個以表達式為主體的 get 訪問器,而 = 運算符指定一個初始值設(shè)定項。 此版本將無參數(shù)構(gòu)造函數(shù)添加到結(jié)構(gòu)。 無參數(shù)構(gòu)造函數(shù)必須調(diào)用主構(gòu)造函數(shù),以便初始化所有主構(gòu)造函數(shù)參數(shù)。
依賴關(guān)系注入
主構(gòu)造函數(shù)的另一個常見用途是指定依賴項注入的參數(shù)。 下面的代碼創(chuàng)建了一個簡單的控制器,使用時需要有一個服務(wù)接口:
public interface IService { Distance GetDistance(); } public class ExampleController(IService service) : ControllerBase { [HttpGet] public ActionResult<Distance> Get() { return service.GetDistance(); } }
主構(gòu)造函數(shù)清楚地指明了類中所需的參數(shù)。 使用主構(gòu)造函數(shù)參數(shù)就像使用類中的任何其他變量一樣。
初始化基類
可以從派生類的主構(gòu)造函數(shù)調(diào)用基類的主構(gòu)造函數(shù)。 這是編寫必須調(diào)用基類中主構(gòu)造函數(shù)的派生類的最簡單方法。 例如,假設(shè)有一個類的層次結(jié)構(gòu),將不同的帳戶類型表示為一個銀行。 基類類似于以下代碼:
public class BankAccount(string accountID, string owner) { public string AccountID { get; } = accountID; public string Owner { get; } = owner; public override string ToString() => $"Account ID: {AccountID}, Owner: {Owner}"; }
一個派生類將呈現(xiàn)一個支票帳戶:
public class CheckAccount(string accountID, string owner, decimal overdraftLimit = 0) : BankAccount(accountID, owner) { public decimal CurrentBalance { get; private set; } = 0; public void Deposit(decimal amount) { if (amount < 0) { throw new ArgumentOutOfRangeException(nameof(amount), "Deposit amount must be positive"); } CurrentBalance += amount; } public void Withdrawal(decimal amount) { if (amount < 0) { throw new ArgumentOutOfRangeException(nameof(amount), "Withdrawal amount must be positive"); } if (CurrentBalance - amount < -overdraftLimit) { throw new InvalidOperationException("Insufficient funds for withdrawal"); } CurrentBalance -= amount; } public override string ToString() => $"Account ID: {AccountID}, Owner: {Owner}, Balance: {CurrentBalance}"; }
總結(jié)
通過合理有效地利用主構(gòu)造函數(shù),我們可以創(chuàng)造出更靈活、更強大、更可控的代碼構(gòu)造。
集合表達式
集合表達式引入了新的語法來創(chuàng)建常見的集合值。 可以使用展開運算符 .. 將其他集合內(nèi)聯(lián)到這些值中。
以下示例演示了集合表達式的使用:
// Create an array: int[] a = [1, 2, 3, 4, 5, 6, 7, 8]; // Create a span Span<int> b = ['a', 'b', 'c', 'd', 'e', 'f', 'h', 'i']; // Create a 2 D array: int[][] twoD = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; // create a 2 D array from variables: int[] row0 = [1, 2, 3]; int[] row1 = [4, 5, 6]; int[] row2 = [7, 8, 9]; int[][] twoDFromVariables = [row0, row1, row2];
總結(jié)
集合表達式使得代碼更簡潔,操作更便捷。
默認 Lambda 參數(shù)
現(xiàn)在可以為 Lambda 表達式的參數(shù)定義默認值。 語法和規(guī)則與將參數(shù)的默認值添加到任何方法或本地函數(shù)相同。
Func<int, string, bool> isTooLong = (int x, string s = "") => s.Length > x;
總結(jié)
默認 Lambda 參數(shù),彌補了Lambda不能設(shè)置默認參數(shù)的缺陷。
任何類型的別名
可以使用 using 別名指令創(chuàng)建任何類型的別名,而不僅僅是命名類型。 這意味著可以為元組類型、數(shù)組類型、指針類型或其他不安全類型創(chuàng)建語義別名。
using Point = (int x, int y);
總結(jié)
它提供了一個簡短的,由開發(fā)者提供的名稱,可以用來替代那些完整的結(jié)構(gòu)形式。
內(nèi)聯(lián)數(shù)組(Inline Arrays)
運行時團隊和其他庫作者使用內(nèi)聯(lián)數(shù)組來提高應(yīng)用的性能。 內(nèi)聯(lián)數(shù)組使開發(fā)人員能夠創(chuàng)建固定大小的 struct 類型數(shù)組。 具有內(nèi)聯(lián)緩沖區(qū)的結(jié)構(gòu)應(yīng)提供類似于不安全的固定大小緩沖區(qū)的性能特征。 你可能不會聲明自己的內(nèi)聯(lián)數(shù)組,但當它們從運行時 API 作為 System.Span 或 System.ReadOnlySpan 對象公開時,你將透明地使用這些數(shù)組。
內(nèi)聯(lián)數(shù)組的聲明類似于以下 struct:
[System.Runtime.CompilerServices.InlineArray(10)] public struct Buffer { private int _element0; }
它們的用法與任何其他數(shù)組類似:
var buffer = new Buffer(); for (int i = 0; i < 10; i++) { buffer[i] = i; } foreach (var i in buffer) { Console.WriteLine(i); }
區(qū)別在于編譯器可以利用有關(guān)內(nèi)聯(lián)數(shù)組的已知信息。 你可能會像使用任何其他數(shù)組一樣使用內(nèi)聯(lián)數(shù)組。
總結(jié)
內(nèi)聯(lián)數(shù)組對性能提高幫助很大。
攔截器(Interceptors)
警告:本次發(fā)布的預(yù)覽版引入了一項叫做interceptors(攔截器)的新功能。這項新功能主要用于一些高級場景,尤其是將會帶來更好的AOT編譯能力。作為.NET 8的實驗性功能,在未來的版本中有可能被修改甚至刪除,因此,它不應(yīng)該在生產(chǎn)環(huán)境中使用。
攔截器是一種方法,該方法可以在編譯時以聲明方式將對可攔截方法的調(diào)用替換為對其自身的調(diào)用。 通過讓攔截器聲明所攔截調(diào)用的源位置,可以進行這種替換。 此過程可以向編譯中(例如在源生成器中)添加新代碼,從而提供更改現(xiàn)有代碼語義的有限能力。
在源生成器中使用攔截器修改現(xiàn)有編譯的代碼,而非向其中添加代碼。 源生成器將對可攔截方法的調(diào)用替換為對攔截器方法的調(diào)用。
總結(jié)
攔截器很強大,進一步了解可以參考下面連接:https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md
使用nameof訪問實例成員
曾經(jīng)為了訪問實例成員,你頻繁地編寫nameof感到非常惱火嗎?好消息是,C# 12 Preview 3為你帶來解決方案。讓我們一起看看這個神奇的功能是如何工作的:
記得以前,當嘗試使用nameof關(guān)鍵字去訪問一個實例字段時,你必須有一個對象的實例,對吧?
現(xiàn)在,告別這些限制吧!有了C# 12 Preview 3,我們只需要類就可以做到這一點。
給出一個實際的例子,讓我們看看這個獨特的特性在這段代碼中是如何發(fā)揮作用的:
internal class NameOf { public string S { get; } = ""; public static int StaticField; public string NameOfLength { get; } = nameof(S.Length); public static void NameOfExamples() { Console.WriteLine(nameof(S.Length)); // 使用`nameof`訪問實例成員 Console.WriteLine(nameof(StaticField.MinValue)); // 使用`nameof`訪問靜態(tài)字段 } [Description($"String {nameof(S.Length)}")] public int StringLength(string s) { return s.Length; } }
你看到nameof如何處理S.Length 和 StaticField.MinValue了嗎?這是C# 12 Preview 3的新特性!你不需要一個實例就可以獲取S.Length的名稱。你也可以用nameof獲取StaticField.MinValue。
簡單來說,想象你有一個叫做"NameOf"的玩具盒。以前,你必須爬進盒子里才能找到你最喜歡的玩具。
但現(xiàn)在呢?你只需要告訴你的魔術(shù)盒你想要什么(比如,你想要的玩具魔方的長度,或者芭蕾舞泰迪熊的最小數(shù)量),它就會給你,都不用進去!
總結(jié)
nameof的增強,讓代碼更少,邏輯更簡單。
參考文檔:https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12
以上就是C#12中的新增功能使用總結(jié)的詳細內(nèi)容,更多關(guān)于C#12新增功能的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Visual Studio C#創(chuàng)建windows服務(wù)程序
用Visual C#創(chuàng)建Windows服務(wù)不是一件困難的事,本文就將指導你一步一步創(chuàng)建一個Windows服務(wù)并使用它,本文主要介紹了Visual Studio C#創(chuàng)建windows服務(wù)程序,感興趣的可以了解一下2024-01-01winfrom 在業(yè)務(wù)層實現(xiàn)事務(wù)控制的小例子
winfrom 在業(yè)務(wù)層實現(xiàn)事務(wù)控制的小例子,需要的朋友可以參考一下2013-03-03C#實現(xiàn)SQL批量插入數(shù)據(jù)到表的方法
這篇文章主要介紹了C#實現(xiàn)SQL批量插入數(shù)據(jù)到表的方法,涉及C#批量操作SQL的相關(guān)技巧,需要的朋友可以參考下2016-04-04