C# 的 GitHub 頁(yè)面上記載了一長(zhǎng)串誘人的想法澄耍,其中一些令人頭疼的問題仍在討論中。如果你想知道C# 10中究竟包含了哪些新功能晌缘,可以等待11 月新版本的發(fā)布齐莲。或者枚钓,你也可以關(guān)注 C# 團(tuán)隊(duì)展示的他們最喜歡的功能。在最近的微軟Build 大會(huì)上瑟押,C# 的首席設(shè)計(jì)師 Mads Torgersen 透漏了一些目前正在進(jìn)行的工作搀捷。以下是該語(yǔ)言的下一個(gè)版本將會(huì)提供的五大新功能。
global using
C# 的源代碼文件開頭一般都會(huì)導(dǎo)入一堆命名空間多望。下面是一個(gè)普通的ASP.NET Web 應(yīng)用程序的代碼片段:
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
? ? {
? ? ? ? ...
? ? }
}
這段代碼的寫法沒有什么特別之處嫩舟。以前,命名空間的導(dǎo)入可以讓我們快速了解某個(gè)類正在使用哪些庫(kù)怀偷。然而如今家厌,這只不過是一堆不得不寫又沒人去看的代碼了。C# 10 引入了一種新模式椎工,允許你使用關(guān)鍵字 global 定義整個(gè)項(xiàng)目的命名空間導(dǎo)入饭于。推薦做法是,將全局導(dǎo)入放在一個(gè)單獨(dú)的文件中(每個(gè)項(xiàng)目一個(gè))维蒙,可以命名為 usings.cs 或imports.cs掰吕。其中的內(nèi)容大致為:
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;
然后就可以簡(jiǎn)化原來的文件了:
using LoggingTestApp.Data;
using Serilog;
namespace LoggingTestApp
{
public class Startup
? ? {
? ? ? ? ...
? ? }
}
Visual Studio會(huì)突出顯示重復(fù)的命名空間(即同時(shí)在全局文件和本地文件中導(dǎo)入的命名空間)。盡管這不是錯(cuò)誤颅痊,但刪除重復(fù)的命名空間可以減少代碼量殖熟,并將注意力集中在特定文件正在使用的特殊命名空間上。
文件范圍的命名空間
C# 10 提供了另一種簡(jiǎn)化代碼的方法:聲明文件范圍的命名空間斑响。文件范圍的命名空間會(huì)自動(dòng)應(yīng)用于整個(gè)文件菱属,而且無需縮進(jìn)。換句話說舰罚,下面這種寫法:
namespace LoggingTestApp
{
public class Startup
? ? {
? ? ? ? ...
? ? }
}
可以變成:
namespace LoggingTestApp;
public class Startup
{
? ? ...
}
如果在使用了文件范圍命名空間的文件中纽门,再添加一個(gè)命名空間塊,則會(huì)創(chuàng)建一個(gè)嵌套命名空間:
namespace Company.Product;
// This block creates the namespace Company.Product.Component
namespace Component
{
}
C# 設(shè)計(jì)者認(rèn)為這個(gè)改動(dòng)可以清理水平空間的浪費(fèi)(就像global using清理了垂直空間的浪費(fèi)一樣)营罢∧せ伲總體目標(biāo)是讓代碼更短、更窄、更簡(jiǎn)潔瘟滨。但這些變化也可以降低新手學(xué)習(xí)C#的難度候醒。結(jié)合global using與文件范圍的命名空間,只需幾行代碼就可以創(chuàng)建出一個(gè)Hello World 控制臺(tái)應(yīng)用程序杂瘸。
空參數(shù)檢查
本著減少樣板代碼的精神倒淫,C# 提供了一個(gè)非常好的新功能:空參數(shù)檢查。你肯定編寫過需要檢查空值的方法败玉。比如敌土,如下代碼:
public UpdateAddress(int personId, Address newAddress)
{
if (newAddress == null)
? ? {
throw new ArgumentNullException("newAddress");
? ? }
? ? ...
}
如今,你只需要在參數(shù)名稱末尾添加“!!”运翼,C#就會(huì)自動(dòng)加入這種空參數(shù)檢查返干。上述代碼可以簡(jiǎn)化為:
public UpdateAddress(int personId, Address newAddress!!)
{
? ? ...
}
現(xiàn)在,如果傳遞一個(gè)空值給 Address血淌,就會(huì)自動(dòng)拋出 ArgumentNullException矩欠。這種細(xì)節(jié)可能看似微不足道,但實(shí)際上這是非常簡(jiǎn)單又很有價(jià)值的優(yōu)化語(yǔ)言的方式悠夯。大量研究表明癌淮,導(dǎo)致程序出錯(cuò)的原因往往是由于非常容易避免的錯(cuò)誤反復(fù)發(fā)生,不是因?yàn)榇a中的概念太復(fù)雜沦补,而是因?yàn)殚喿x代碼很累乳蓄,而人類的注意力有限。減少代碼量可以減少審查代碼所需的時(shí)間夕膀,處理代碼所需的認(rèn)知負(fù)荷虚倒,以及由于注意力減弱而忽略某些錯(cuò)誤的可能性。
required 屬性
以前产舞,我們只能通過類構(gòu)造函數(shù)來確保正確地創(chuàng)建對(duì)象裹刮。如今,我們經(jīng)常使用更加輕量級(jí)的結(jié)構(gòu)庞瘸,比如下面這個(gè)記錄中自動(dòng)實(shí)現(xiàn)的屬性:
public record Employee
{
? ? public string Name { get; init; }
? ? public decimal YearlySalary { get; init; }
? ? public DateTime HiredDate{ get; init; }
}
在創(chuàng)建這類輕量級(jí)對(duì)象的實(shí)例時(shí)捧弃,我們可能會(huì)使用對(duì)象的初始化語(yǔ)法:
var theNewGuy = new Employee
{
? ?Name = "Dave Bowman",
? ?YearlySalary = 100000m,
? ?HiredDate = DateTime.Now()
};
但是,如果你的對(duì)象中的某些屬性是必須的擦囊,該怎么辦违霞?你可以像以前一樣,添加一個(gè)構(gòu)造函數(shù)瞬场,但如此一來就需要添加更多的樣板代碼了买鸽。此外,將值從一個(gè)參數(shù)復(fù)制到屬性也是另一個(gè)很容易理解但很常見的錯(cuò)誤贯被。C# 10 引入的關(guān)鍵字 required 可以消滅這類問題:
public record Employee
{
? ? public required string Name { get; init; }
? ? public decimal YearlySalary { get; init; }
? ? public DateTime HiredDate{ get; init; }
}
如此一來眼五,如果不設(shè)置 Name 屬性就無法創(chuàng)建 Employee 了妆艘。
關(guān)鍵字field
多年來,為了通過自動(dòng)實(shí)現(xiàn)屬性簡(jiǎn)化代碼看幼,C# 團(tuán)隊(duì)做出了大量努力批旺,上面的 Employee 記錄就是一個(gè)很好的例子,它使用 get 和 init 關(guān)鍵字聲明了三個(gè)不可變的屬性诵姜。數(shù)據(jù)存儲(chǔ)在三個(gè)私有字段中汽煮,但這些字段都是自動(dòng)創(chuàng)建的,無需人工干預(yù)棚唆。而且你永遠(yuǎn)不會(huì)看到這些字段暇赤。自動(dòng)實(shí)現(xiàn)的屬性很棒,但它們的作用也僅限于此宵凌。當(dāng)無法使用自動(dòng)實(shí)現(xiàn)的屬性時(shí)鞋囊,你就必須添加支持字段到類,并編寫正常的屬性方法瞎惫,就像回到 C# 2一樣溜腐。但是 C# 10中提供了一個(gè)關(guān)鍵字field,可以自動(dòng)創(chuàng)建支持字段微饥。例如逗扒,假設(shè)你想創(chuàng)建一個(gè)記錄古戴,用于處理初始屬性值欠橘。在下面的代碼中,我們對(duì) Employee 類進(jìn)行了一些修改现恼,確保HiredDate 字段只包含來自 DateTime 對(duì)象的日期信息(不包含時(shí)間信息):
public record Employee
{
? ? public required string Name { get; init; }
? ? public decimal YearlySalary { get; init; }
? ? public DateTime HiredDate{ get; init => field = value.Date(); }
}
這段代碼非常整潔肃续、簡(jiǎn)單,而且很接近聲明式叉袍。你可以使用關(guān)鍵字 field 訪問 get始锚、set 或 init 中的字段。而且喳逛,你可能需要驗(yàn)證某個(gè)屬性瞧捌,就像驗(yàn)證普通類中的屬性一樣:
private string _firstName;
public string FirstName
{
? ? get
? ? {
? ? ? ? return _firstName;
? ? }
? ? set
? ? {
? ? ? ? if (value.Trim() == "")
? ? ? ? ? ? throw new ArgumentException("No blank strings");
? ? ? ? _firstName = value;
? ? }
}
你可以使用 field 來驗(yàn)證自動(dòng)實(shí)現(xiàn)的屬性:
public string FirstName {get;
? ? set
? ? {
? ? ? ? if (value.Trim() == "")
? ? ? ? ? ? throw new ArgumentException("No blank strings");
? ? ? ? field = value;
? ? }
}
本質(zhì)上,只要不需要修改屬性的數(shù)據(jù)類型润文,就不需要自行聲明支持字段姐呐。
總結(jié)
當(dāng)然,C# 10中的新功能肯定不止這個(gè)五個(gè)典蝌。還有一些表達(dá)式方面的變化曙砂,以及一個(gè)有爭(zhēng)議的變動(dòng):在接口中定義靜態(tài)成員。我們只有耐心等待了骏掀○海總體來看柱告,C# 10 的發(fā)展重點(diǎn)很明確,即減少代碼量笑陈,提供更多便利性际度,減輕開發(fā)人員的負(fù)擔(dān) 作者:微軟MVP-Eleven https://www.bilibili.com/read/cv12057334 出處:bilibili