※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!
C# 9.0正在形成,我想分享我們對添加到該語言下個版本的一些主要功能的看法。對於每個新版本的 C#,我們努力使常見的編碼方案更加清晰和簡單,C# 9.0 也不例外。這次的一個特別重點是支持數據形狀的簡潔和不可變表示。
讓我們潛入吧!
1 僅可初始化的屬性
對象初始化器是非常好用的。它們為類型實例化提供了一種非常靈活且可讀的格式來創建對象,尤其是對於一次創建特別大的嵌套對象來說。下面是一個簡單的例子:
new Person { FirstName = "Scott", LastName = "Hunter" }
對象初始化也使用戶不必編寫大量構造函數,要做的就是編寫一些屬性!
public class Person { public string FirstName { get; set; } public string LastName { get; set; } }
今天,一個很大的限制是,屬性必須是可修改的,對象初始化器是這樣工作的:首先調用對象的構造函數(默認為無參的構造函數),然後分配給屬性設置器(property setter)。
僅可初始化屬性修改了這一點!它們引入了一個 init 訪問器,該訪問器是set訪問器的變體,只能在對象初始化期間調用:
public class Person { public string FirstName { get; init; } public string LastName { get; init; } }
使用此聲明,除了初始化外,之後任何後續賦值給 FirstName 和 LastName 屬性都是一個錯誤。
因為init訪問器只能在初始化期間訪問,因此他們允許修改封閉類型中的只讀字段,就像在構造函數中那樣:
public class Person { private readonly string firstName; private readonly string lastName; public string FirstName { get => firstName; init => firstName = (value ?? throw new ArgumentNullException(nameof(FirstName))); } public string LastName { get => lastName; init => lastName = (value ?? throw new ArgumentNullException(nameof(LastName))); } }
2 記錄
如果要使單個屬性不可變,則僅可初始化屬性非常適合。如果希望整個對象不可變且像值類型一樣,則應考慮將其聲明為記錄:
public data class Person { public string FirstName { get; init; } public string LastName { get; init; } }
類聲明中的data關鍵字將其標記為記錄。這賦予它幾個類似價值類型的行為,我們將在下面深入探討這些行為。一般來說,記錄更被視為”值”(純數據), 而不是作為對象。您可以通過創建新記錄表示新狀態來表示隨時間的變化。它們不是由標識定義,而是由其內容定義。
2.1 With表達式
使用不可變數據時,一種常見模式是從現有值創建新值以表示新狀態。例如,如果我們更改LastName,我們會將其表示為一個新對象,該對象是舊對象的副本,但LastName不同。這種技術通常被稱為非破壞性修改。記錄這種特性表示的是Person在給定時間的狀態。
為了適應這種編程風格,記錄允許一種新的表達式——with:
var otherPerson = person with{LastName="Hanselman"};
with表達式使用對象初始化器語法來說明新對象與舊對象的不同內容。您可以指定多個屬性。
記錄隱式定義一個受保護的”複製構造函數”-一個構造函數,它獲取現有記錄對象,並逐個將其字段複製到新的對象:
protected Person(Person original){/* copy all the fields */}// generated
with 表達式會導致調用複製構造函數,然後在上面應用對象初始化器以相應地更改屬性。
如果您不喜歡生成的複製構造函數的默認行為,則可以改為定義自己的行為,該行為將由with表達式選取。
2.2 基於值的相等性
所有對象都從Object繼承 Equals(object)。結構將其重寫為具有”基於價值的相等性”,通過遞歸地調用Equals來比較結構的每個字段。記錄也執行相同的操作。這意味着,根據其”值”,兩個記錄對象可以彼此相等,而不必是同一對象。例如:
var originalPerson = otherPerson with { LastName = "Hunter" };
現在 ReferenceEquals(person, originalPerson) = false(這兩個不是一個對象)但是Equals(person, originalPerson) = true (他們有相同的值)。
如果您不喜歡生成的 Equals 重寫的默認逐字段比較行為,則可以改為編寫自己的字段比較行為。你只需要小心,你了解基於值的相等在記錄中是如何工作的,特別是當涉及繼承時。
除了重寫Equals 外,還有 GetHashCode()。
2.3 數據成員
記錄絕大多數都是不可變的,只有隻讀初始化器可以通過with表達式進行非破壞性修改。為了針對這種常見情況進行優化,記錄在聲明時會更改string FirstName這類成員聲明的行為。與其他類和結構聲明中的隱式private字段不同,在記錄中,這被視為public的、僅可初始化的自動屬性的縮寫!因此:
public data classPerson { string FirstName; string LastName; }
與
public data classPerson { public string FirstName{get; init;} public string LastName{get; init;} }
是相同的。
我們認為這有助於做出漂亮而清晰的記錄聲明。如果您真的需要私有字段,只需顯式地添加private修飾符:
private string firstName;
2.4 基於位置的記錄
有時,對記錄採用更為位置化的方法是有用的,在這種方法中,記錄的內容通過構造函數參數的位置給出,並且可以通過解構函數來提取。
可以在記錄中指定自己的構造函數和解構函數:
public data classPerson { string FirstName; string LastName; public Person(string firstName,string lastName) =>(FirstName,LastName)=(firstName, lastName); public void Deconstruct(out string firstName,out string lastName) =>(firstName, lastName)=(FirstName,LastName); }
上面代碼可以簡寫為:
※想知道最厲害的網頁設計公司嚨底家"!
RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌
public data class Person(string FirstName,string LastName);
這將聲明public的僅初始化的自動屬性以及構造函數和解構函數,以便您可以編寫:
var person =new Person("Scott","Hunter");// positional construction var(f, l)= person; // positional deconstruction
如果您不喜歡生成的自動屬性,則可以改為定義自己的同名屬性,生成的構造函數和解構函數將使用該屬性。
2.5 記錄的改變引發的問題
想象一下,將記錄對象放入字典中。再次找到它取決於 Equal 和GetHashCode。如果記錄改變其狀態,它也會改變它等於什麼!我們可能再也找不到了!在哈希表實現中,它甚至可能損壞數據結構,因為定位基於的是”到達哈希表時”的哈希值!
雖然可以通過重寫一些內部方法來改變這種默認的行為,但其工作量也是相當巨大的。
2.6 with表達式與繼承
public data class Person{string FirstName;string LastName;} public data class Student:Person{int ID;} Person person =new Student{FirstName="Scott",LastName="Hunter", ID =GetNewId()}; otherPerson = person with{LastName="Hanselman"};
在最後一行上使用with表達式時,編譯器不知道person實際上包含了一個Student。而且,即使otherPerson實際上不是”Student”對象,它也不是一個正確的副本,該對象與複製的第一個對象具有相同的ID。
記錄有一個隱藏的虛方法,它委託”克隆”整個對象。每個派生記錄類型都重寫此方法以調用該類型的複製構造函數,以及派生鏈上的複製構造函數直到基類記錄的複製構造函數。with表達式只需調用隱藏的”克隆”方法,並將對象初始化器應用於結果。
2.7 值相等與繼承
與with表達式的實現類似,基於值的相等性也必須是”虛擬”的,即Student需要比較所有字段,即使比較時能夠得知類型是基類型Person。這是很容易通過重寫已經虛擬的Equals方法實現的。
但是,相等還有一個挑戰:如果比較兩種不同的Person,該怎麼辦?我們不能讓其中一個決定是否相等:相等應該是對稱的,所以無論兩個對象中哪個是第一個,結果都應該是相同的。換句話說,他們必須就適用的相等達成一致!
說明問題的示例:
Person person1 =new Person{FirstName="Scott",LastName="Hunter"}; Person person2 =new Student{FirstName="Scott",LastName="Hunter", ID =GetNewId()};
這兩個對象彼此相等嗎?person1可能會這樣認為,因為person2有所有的Person的構造,但person2會認為與person1不同!我們需要確保他們都同意他們是不同的對象。
C# 會自動為您處理。它的實現方式是每個記錄都有一個”EqualityContract”的虛擬受保護屬性。每個派生記錄都會重寫它,為了比較相等,兩個對象必須具有相同的EqualityContract。
3 簡化頂級程序
之前我們這樣寫代碼:
using System; class Program { static void Main() { Console.WriteLine("Hello World!"); } }
現在您可以選擇在頂層編寫主程序:
using System; Console.WriteLine("Hello World!");
支持任何語句,但必須在using之後以及文件中的任何類型或命名空間聲明之前,並且只能在一個文件中執行此操作,就像目前只能有一個Main方法一樣。如果要返回狀態代碼,可以執行此操作。如果你想await,你可以這樣做。如果要訪問命令行參數,可以訪問args參數。
局部函數是語句的一種形式,在頂級程序中也允許使用。從頂級語句部分以外的任何位置調用它們都是錯誤的。
4 改進模式匹配
在 C# 9.0 中添加了幾種新類型的模式。例如:
public static decimal CalculateToll(object vehicle) => vehicle switch { ... DeliveryTruck t when t.GrossWeightClass > 5000 => 10.00m + 5.00m, DeliveryTruck t when t.GrossWeightClass < 3000 => 10.00m - 2.00m, DeliveryTruck _ => 10.00m, _ => throw new ArgumentException("Not a known vehicle type", nameof(vehicle)) };
4.1 簡單類型模式
目前,類型模式需要在類型匹配時聲明一個標識符,即使該標識符是一個_,比如 DeliveryTruck _。新語法不用了,可以簡寫為:
DeliveryTruck => 10.00m,
4.2 關係模式
C#9.0引入了對應於關係運算符<、<=等的模式。因此,新語法可以這樣寫:
DeliveryTruck t when t.GrossWeightClass switch { > 5000 => 10.00m + 5.00m, < 3000 => 10.00m - 2.00m, ... },
4.3 邏輯模式
最後,可以將模式與邏輯運算(and 、or、not)符組合起來,並將其拼寫為單詞,以避免與表達式中使用的運算符混淆。例如:
DeliveryTruck t when t.GrossWeightClass switch { < 3000 => 10.00m - 2.00m, >= 3000 and <= 5000 => 10.00m, > 5000 => 10.00m + 5.00m, },
not的常見用法是將其應用於判空。例如:
not null => throw new ArgumentException($"Not a known vehicle type: {vehicle}", nameof(vehicle)), null => throw new ArgumentNullException(nameof(vehicle))
還有,if (!(e is Customer)) { … }在新語法中,可以寫為if (e is not Customer) { … }
5 目標類型
”Target typing”是當表達式從使用位置的上下文中獲取其類型時,我們使用的術語。C# 9.0支持新的類型推斷。
5.1 new
新語法中,如果是明確的類型,則在使用new時,可以不聲明類型了。比如:
Point p = new (3, 5);
5.2 ?? and ?:
目前,??與?:如果分支之間不是同一類型會報錯。新語法下,如果兩個分支都可以轉換為目標類型則是允許的:
Person person = student ?? customer; // Shared base type int? result = b ? 0 : null; // nullable value type
6 改進協變
有時,派生類中的方法返還比基類中的聲明更具體的類型是很有用的。C# 9.0 允許:
abstract class Animal { public abstract Food GetFood(); ... } class Tiger : Animal { public override Meat GetFood() => ...; }
此外,還要很多新的改進,讓我們拭目以待吧。
原文鏈接
https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/?utm_source=vs_developer_news&utm_medium=referral
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊