C#12中的新增功能使用總結(jié)
新的 C# 12 功能在預(yù)覽版中已經(jīng)引入. 您可以使用最新的 Visual Studio 預(yù)覽版或最新的 .NET 8 預(yù)覽版 SDK 來嘗試這些功能。以下是一些新引入的功能:
- 主構(gòu)造函數(shù)
- 集合表達(dá)式
- 默認(rèn) Lambda 參數(shù)
- 任何類型的別名
- 內(nèi)聯(lián)數(shù)組
- 攔截器
- 使用nameof訪問實(shí)例成員
主構(gòu)造函數(shù)
現(xiàn)在可以在任何 class 和 struct 中創(chuàng)建主構(gòu)造函數(shù)。 主構(gòu)造函數(shù)不再局限于 record 類型。 主構(gòu)造函數(shù)參數(shù)都在類的整個(gè)主體的范圍內(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ù)生成公共屬性。 對(duì)于主構(gòu)造函數(shù)參數(shù),非記錄類和結(jié)構(gòu)可能并不總是需要此行為。
主構(gòu)造函數(shù)的參數(shù)位于聲明類型的整個(gè)主體中。 它們可以初始化屬性或字段。 它們可用作方法或局部函數(shù)中的變量。 它們可以傳遞給基本構(gòu)造函數(shù)。
主構(gòu)造函數(shù)指示這些參數(shù)對(duì)于類型的任何實(shí)例是必需的。 任何顯式編寫的構(gòu)造函數(shù)都必須使用 this(...) 初始化表達(dá)式語法來調(diào)用主構(gòu)造函數(shù)。 這可確保主構(gòu)造函數(shù)參數(shù)絕對(duì)由所有構(gòu)造函數(shù)分配。 對(duì)于任何 class 類型(包括 record class 類型),當(dāng)主構(gòu)造函數(shù)存在時(shí),不會(huì)發(fā)出隱式無參數(shù)構(gòu)造函數(shù)。 對(duì)于任何 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)用場(chǎng)景
初始化屬性
以下代碼初始化從主構(gòu)造函數(shù)參數(shù)計(jì)算的兩個(gè)只讀屬性:
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);
}前面的代碼演示了用于初始化計(jì)算的只讀屬性的主構(gòu)造函數(shù)。 Direction 和 Magnitude 的字段初始值設(shè)定項(xiàng)使用主構(gòu)造函數(shù)參數(shù)。 主構(gòu)造函數(shù)參數(shù)不會(huì)在結(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);
}
}當(dāng)需要參數(shù)來初始化字段或?qū)傩詴r(shí),利用新功能可以更輕松地使用字段初始值設(shè)定項(xiàng)。
創(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 組件。 這就需要在訪問時(shí)計(jì)算 Magnitude 和 Direction 屬性。 => 運(yùn)算符指定一個(gè)以表達(dá)式為主體的 get 訪問器,而 = 運(yùn)算符指定一個(gè)初始值設(shè)定項(xiàng)。 此版本將無參數(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ù)的另一個(gè)常見用途是指定依賴項(xiàng)注入的參數(shù)。 下面的代碼創(chuàng)建了一個(gè)簡單的控制器,使用時(shí)需要有一個(gè)服務(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è)有一個(gè)類的層次結(jié)構(gòu),將不同的帳戶類型表示為一個(gè)銀行。 基類類似于以下代碼:
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}";
}一個(gè)派生類將呈現(xiàn)一個(gè)支票帳戶:
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)造出更靈活、更強(qiáng)大、更可控的代碼構(gòu)造。
集合表達(dá)式
集合表達(dá)式引入了新的語法來創(chuàng)建常見的集合值。 可以使用展開運(yùn)算符 .. 將其他集合內(nèi)聯(lián)到這些值中。
以下示例演示了集合表達(dá)式的使用:
// 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é)
集合表達(dá)式使得代碼更簡潔,操作更便捷。
默認(rèn) Lambda 參數(shù)
現(xiàn)在可以為 Lambda 表達(dá)式的參數(shù)定義默認(rèn)值。 語法和規(guī)則與將參數(shù)的默認(rèn)值添加到任何方法或本地函數(shù)相同。
Func<int, string, bool> isTooLong = (int x, string s = "") => s.Length > x;
總結(jié)
默認(rèn) Lambda 參數(shù),彌補(bǔ)了Lambda不能設(shè)置默認(rèn)參數(shù)的缺陷。
任何類型的別名
可以使用 using 別名指令創(chuàng)建任何類型的別名,而不僅僅是命名類型。 這意味著可以為元組類型、數(shù)組類型、指針類型或其他不安全類型創(chuàng)建語義別名。
using Point = (int x, int y);
總結(jié)
它提供了一個(gè)簡短的,由開發(fā)者提供的名稱,可以用來替代那些完整的結(jié)構(gòu)形式。
內(nèi)聯(lián)數(shù)組(Inline Arrays)
運(yùn)行時(shí)團(tuán)隊(duì)和其他庫作者使用內(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ū)的性能特征。 你可能不會(huì)聲明自己的內(nèi)聯(lián)數(shù)組,但當(dāng)它們從運(yùn)行時(shí) API 作為 System.Span 或 System.ReadOnlySpan 對(duì)象公開時(shí),你將透明地使用這些數(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ù)組的已知信息。 你可能會(huì)像使用任何其他數(shù)組一樣使用內(nèi)聯(lián)數(shù)組。
總結(jié)
內(nèi)聯(lián)數(shù)組對(duì)性能提高幫助很大。
攔截器(Interceptors)
警告:本次發(fā)布的預(yù)覽版引入了一項(xiàng)叫做interceptors(攔截器)的新功能。這項(xiàng)新功能主要用于一些高級(jí)場(chǎng)景,尤其是將會(huì)帶來更好的AOT編譯能力。作為.NET 8的實(shí)驗(yàn)性功能,在未來的版本中有可能被修改甚至刪除,因此,它不應(yīng)該在生產(chǎn)環(huán)境中使用。
攔截器是一種方法,該方法可以在編譯時(shí)以聲明方式將對(duì)可攔截方法的調(diào)用替換為對(duì)其自身的調(diào)用。 通過讓攔截器聲明所攔截調(diào)用的源位置,可以進(jìn)行這種替換。 此過程可以向編譯中(例如在源生成器中)添加新代碼,從而提供更改現(xiàn)有代碼語義的有限能力。
在源生成器中使用攔截器修改現(xiàn)有編譯的代碼,而非向其中添加代碼。 源生成器將對(duì)可攔截方法的調(diào)用替換為對(duì)攔截器方法的調(diào)用。
總結(jié)
攔截器很強(qiáng)大,進(jìn)一步了解可以參考下面連接:https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md
使用nameof訪問實(shí)例成員
曾經(jīng)為了訪問實(shí)例成員,你頻繁地編寫nameof感到非常惱火嗎?好消息是,C# 12 Preview 3為你帶來解決方案。讓我們一起看看這個(gè)神奇的功能是如何工作的:
記得以前,當(dāng)嘗試使用nameof關(guān)鍵字去訪問一個(gè)實(shí)例字段時(shí),你必須有一個(gè)對(duì)象的實(shí)例,對(duì)吧?
現(xiàn)在,告別這些限制吧!有了C# 12 Preview 3,我們只需要類就可以做到這一點(diǎn)。
給出一個(gè)實(shí)際的例子,讓我們看看這個(gè)獨(dú)特的特性在這段代碼中是如何發(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`訪問實(shí)例成員
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的新特性!你不需要一個(gè)實(shí)例就可以獲取S.Length的名稱。你也可以用nameof獲取StaticField.MinValue。
簡單來說,想象你有一個(gè)叫做"NameOf"的玩具盒。以前,你必須爬進(jìn)盒子里才能找到你最喜歡的玩具。
但現(xiàn)在呢?你只需要告訴你的魔術(shù)盒你想要什么(比如,你想要的玩具魔方的長度,或者芭蕾舞泰迪熊的最小數(shù)量),它就會(huì)給你,都不用進(jìn)去!
總結(jié)
nameof的增強(qiáng),讓代碼更少,邏輯更簡單。
參考文檔:https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12
以上就是C#12中的新增功能使用總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于C#12新增功能的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#使用OpenCvSharp實(shí)現(xiàn)透視變換功能
這篇文章主要為大家詳細(xì)介紹了C#如何使用OpenCvSharp實(shí)現(xiàn)透視變換的功能,文中的示例代碼簡潔易懂,具有一定的學(xué)習(xí)價(jià)值,需要的小伙伴可以參考下2023-11-11
Visual Studio C#創(chuàng)建windows服務(wù)程序
用Visual C#創(chuàng)建Windows服務(wù)不是一件困難的事,本文就將指導(dǎo)你一步一步創(chuàng)建一個(gè)Windows服務(wù)并使用它,本文主要介紹了Visual Studio C#創(chuàng)建windows服務(wù)程序,感興趣的可以了解一下2024-01-01
winfrom 在業(yè)務(wù)層實(shí)現(xiàn)事務(wù)控制的小例子
winfrom 在業(yè)務(wù)層實(shí)現(xiàn)事務(wù)控制的小例子,需要的朋友可以參考一下2013-03-03
C#實(shí)現(xiàn)SQL批量插入數(shù)據(jù)到表的方法
這篇文章主要介紹了C#實(shí)現(xiàn)SQL批量插入數(shù)據(jù)到表的方法,涉及C#批量操作SQL的相關(guān)技巧,需要的朋友可以參考下2016-04-04

