【譯】Welcome to C# 9.0_網頁設計公司

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

  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

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊