C# 10 必須知道的五大新功能

C# 10 必須知道的五大新功能

C# 的 GitHub 頁面上記載了一長串誘人的想法,其中一些令人頭痛的問題仍在討論中。

最後更新 2021/12/12 上午10:01
Matthew MacDonald
預計閱讀 8 分鐘
分類
.NET
標籤
.NET C# GitHub C# 10

C# 的 GitHub 頁面上記載了一長串誘人的想法,其中一些令人頭痛的問題仍在討論中。如果你想知道 C# 10 中究竟包含了哪些新功能,可以等待 11 月新版本的發佈。或者,你也可以關注 C# 團隊展示的他們最喜歡的功能。在最近的微軟 Build 大會上,C# 的首席設計師 Mads Torgersen 透露了一些目前正在進行的工作。以下是該語言的下一版本將會提供的五大新功能。

1. global using

C# 的原始碼檔案開頭一般都會導入一堆命名空間。下面是一個普通的 ASP.NET Web 應用程式的程式碼片段:

using LoggingTestApp.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace LoggingTestApp
{
	public class Startup
    {
        ...
    }
}

這段程式碼的寫法沒有什麼特別之處。以前,命名空間的導入可以讓我們快速了解某個類別正在使用哪些函式庫。然而如今,這只不過是一堆不得不寫又沒人去看的程式碼了。

C# 10 引入了一種新模式,允許你使用關鍵字 global 定義整個專案的命名空間導入。推薦做法是,將全域導入放在一個單獨的檔案中(每個專案一個),可以命名為 usings.cs 或 imports.cs。其中的內容大致為:

global using Microsoft.AspNetCore.Builder;
global using Microsoft.AspNetCore.Hosting;
global using Microsoft.AspNetCore.HttpsPolicy;
global using Microsoft.AspNetCore.Identity;
global using Microsoft.AspNetCore.Identity.UI;
global using Microsoft.EntityFrameworkCore;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Hosting;
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Threading.Tasks;

然後就可以簡化原來的檔案了:

using LoggingTestApp.Data;
using Serilog;
namespace LoggingTestApp
{
	public class Startup
    {
        ...
    }
}

Visual Studio 會突出顯示重複的命名空間(即同時在全域檔案和本地檔案中導入的命名空間)。儘管這不是錯誤,但刪除重複的命名空間可以減少程式碼量,並將注意力集中在特定檔案正在使用的特殊命名空間上。

2. 檔案範圍的命名空間

C# 10 提供了另一種簡化程式碼的方法:宣告檔案範圍的命名空間。檔案範圍的命名空間會自動應用於整個檔案,而且無需縮排。

換句話說,下面這種寫法:

namespace LoggingTestApp
{
	public class Startup
    {
        ...
    }
}

可以變成:

namespace LoggingTestApp;
public class Startup
{
    ...
}

如果在使用了檔案範圍命名空間的檔案中,再添加一個命名空間區塊,則會建立一個巢狀命名空間:

namespace Company.Product;
// This block creates the namespace Company.Product.Component
namespace Component
{
}

C# 設計者認為這個改動可以清理水平空間的浪費(就像 global using 清理了垂直空間的浪費一樣)。總體目標是讓程式碼更短、更窄、更簡潔。但這些變化也可以降低新手學習 C# 的難度。結合 global using 與檔案範圍的命名空間,只需幾行程式碼就可以建立出一個 Hello World 控制台應用程式。

3. 空參數檢查

本著減少樣板程式碼的精神,C# 提供了一個非常好的新功能:空參數檢查。你肯定編寫過需要檢查空值的方法。比如,如下程式碼:

public UpdateAddress(int personId, Address newAddress)
{
	if (newAddress == null)
    {
		throw new ArgumentNullException("newAddress");
    }
    ...
}

如今,你只需要在參數名稱末尾添加「!!」,C# 就會自動加入這種空參數檢查。上述程式碼可以簡化為:

public UpdateAddress(int personId, Address newAddress!!)
{
    ...
}

現在,如果傳遞一個空值給 Address,就會自動拋出 ArgumentNullException。

這種細節可能看似微不足道,但實際上這是非常簡單又很有價值的優化語言的方式。大量研究表明,導致程式出錯的原因往往是因為非常容易避免的錯誤反覆發生,不是因為程式碼中的概念太複雜,而是因為閱讀程式碼很累,而人類的注意力有限。減少程式碼量可以減少審查程式碼所需的時間,處理程式碼所需的認知負荷,以及由於注意力減弱而忽略某些錯誤的可能性。

4. required 屬性

以前,我們只能透過類別建構函式來確保正確地建立物件。如今,我們經常使用更加輕量級的結構,比如下面這個記錄中自動實作的屬性:

public record Employee
{
    public string Name { get; init; }
    public decimal YearlySalary { get; init; }
    public DateTime HiredDate{ get; init; }
}

在建立這類輕量級物件的實例時,我們可能會使用物件的初始化語法:

var theNewGuy = new Employee
{
    Name = "Dave Bowman",
    YearlySalary = 100000m,
    HiredDate = DateTime.Now()
};

但是,如果你的物件中的某些屬性是必須的,該怎麼辦?你可以像以前一樣,添加一個建構函式,但如此一來就需要添加更多的樣板程式碼了。此外,將值從一個參數複製到屬性也是另一個很容易理解但很常見的錯誤。

C# 10 引入的關鍵字 required 可以消滅這類問題:

public record Employee
{
    public required string Name { get; init; }
    public decimal YearlySalary { get; init; }
    public DateTime HiredDate{ get; init; }
}

如此一來,如果不設定 Name 屬性就無法建立 Employee 了。

5. 關鍵字 field

多年來,為了透過自動實作屬性簡化程式碼,C# 團隊做出了大量努力,上面的 Employee 記錄就是一個很好的例子,它使用 get 和 init 關鍵字宣告了三個不可變的屬性。資料儲存在三個私有欄位中,但這些欄位都是自動建立的,無需人工干預。而且你永遠不會看到這些欄位。

自動實作的屬性很棒,但它們的作用也只限於此。當無法使用自動實作的屬性時,你就必須添加支援欄位到類別,並編寫正常的屬性方法,就像回到 C# 2 一樣。但是 C# 10 中提供了一個關鍵字 field,可以自動建立支援欄位。

例如,假設你想建立一個記錄,用於處理初始屬性值。在下面的程式碼中,我們對 Employee 類別進行了一些修改,確保 HiredDate 欄位只包含來自 DateTime 物件的日期資訊(不包含時間資訊):

public record Employee
{
    public required string Name { get; init; }
    public decimal YearlySalary { get; init; }
    public DateTime HiredDate{ get; init => field = value.Date(); }
}

這段程式碼非常整潔、簡單,而且很接近宣告式。

你可以使用關鍵字 field 存取 get、set 或 init 中的欄位。而且,你可能需要驗證某個屬性,就像驗證普通類別中的屬性一樣:

private string _firstName;
public string FirstName
{
    get
    {
        return _firstName;
    }
    set
    {
        if (value.Trim() == "")
            throw new ArgumentException("No blank strings");
        _firstName = value;
    }
}

你可以使用 field 來驗證自動實作的屬性:

public string FirstName {get;
    set
    {
        if (value.Trim() == "")
            throw new ArgumentException("No blank strings");
        field = value;
    }
}

本質上,只要不需要修改屬性的資料型別,就不需要自行宣告支援欄位。

6. 總結

當然,C# 10 中的新功能肯定不止這五個。還有一些運算式方面的變化,以及一個有爭議的變動:在介面中定義靜態成員。我們只有耐心等待了。

總體來看,C# 10 的發展重點很明確,即減少程式碼量,提供更多便利性,減輕開發人員的負擔。

原文作者:Matthew MacDonald

原文連結:https://medium.com/young-coder/a-closer-look-at-5-new-features-in-c-10-f99738b0158e

譯者:彎月

責編:歐陽姝黎

出品:CSDN(ID:CSDNnews)

聲明:本文由 CSDN 翻譯,轉載請註明來源。

繼續探索

延伸閱讀

更多文章
同分類 / 同標籤 2022/2/12

C# 10 的新功能

我們很開心地宣布,C# 10 作為 .NET 6 和 Visual Studio 2022 的一部分已經發佈了。

繼續閱讀