一探即將到來(lái)的 C# 10

本來(lái)因?yàn)閼胁幌雽?xiě)這篇文章,但是不少人表示有興趣,于是最后決定還是寫(xiě)一下。

.NET 6 最近幾個(gè)預(yù)覽版一直都在開(kāi)發(fā)體驗(yàn)(如 hot reload、linker 等)、平臺(tái)支持(如 Android、iOS 等)、工具鏈(如 crossgen2、PGO 工具和 wasm 的 AOT 等)、JIT(如 LSRA、Jump threading、PGO 和 guarded devirtualization 以及使 struct 保持在寄存器上等)、GC(如 Regions 等)以及 BCL(如 TimeOnly、DateOnly 以及 Json DOM 等)方面做改進(jìn),然而卻一直沒(méi)有公布 C# 10 的任何內(nèi)容,即使在 Build 2021 大會(huì)上也沒(méi)有提及這方面內(nèi)容。然而實(shí)際上不少特性的實(shí)現(xiàn)已經(jīng)接近尾聲了,那么讓我們提前來(lái)看看 C# 10 可以為我們帶來(lái)什么東西。

當(dāng)然,不是所有下面列出的特性都一定會(huì)進(jìn)入 C# 10,也可能會(huì)和本文有所出入,我在每一個(gè)特性后面加了一個(gè)百分比表示最終實(shí)裝的可能性,僅供參考。

Backing Fields(60%)#

相信不少人在編寫(xiě)屬性的時(shí)候,因?yàn)樽詣?dòng)屬性不能滿足自己的需求于是不得不改回手動(dòng)實(shí)現(xiàn)屬性,這個(gè)時(shí)候總是會(huì)想“如果能不用手動(dòng)寫(xiě)字段的定義就好了”,現(xiàn)在這個(gè)夢(mèng)想成真了:

Copy

private int myInt;

public int MyInt { get => myInt; set => myInt = value; }

C# 10 中新增了一個(gè) field,當(dāng)使用它時(shí)會(huì)自動(dòng)為屬性創(chuàng)建字段定義,不需要再手動(dòng)定義字段了,因此也叫做半自動(dòng)屬性。

Copy

public int MyInt { get => field; set => field = value; }

Record Structs(100%)#

Records 此前只支持 class,但是現(xiàn)在同樣支持 struct 啦,于是你可以定義值類型的 record,避免不必要的堆內(nèi)存分配:

Copy

record struct Point(int X, int Y);

with on Anonymous Objects(80%)#

此前 with 只能配合 records 使用,但是現(xiàn)在它被擴(kuò)展到了匿名對(duì)象上,你可以通過(guò) with 來(lái)創(chuàng)建匿名對(duì)象的副本并且修改它的值啦:

Copy

var foo = new { A = 1, B = "test", C = 4.4 };

var bar = foo with { A = 3 };

Console.WriteLine((bar.A, bar.B, bar.C)); // (3, test, 4.4)

Global Usings(80%)#

此前 using 語(yǔ)句的生效范圍是單個(gè)文件的,如果你想使用一些 namespace,或者定義一系列的類型別名在整個(gè)項(xiàng)目?jī)?nèi)使用,那么你就需要這樣:

Copy

using System.Linq;

using static System.Math;

using i32 = System.Int32;

using i64 = System.Int64;

然后在每個(gè)文件中重復(fù)一遍。但是現(xiàn)在不需要了,你可以定義全局的 using 了:

Copy

global using System.Linq;

global using static System.Math;

global using i32 = System.Int32;

global using i64 = System.Int64;

然后在整個(gè)項(xiàng)目中就都可以用了。

File Scoped Namespace(90%)#

C# 10 開(kāi)始你將能夠在文件頂部指定該文件的 namespace,而不需要寫(xiě)一個(gè) namespace 然后把其他代碼都嵌套在大括號(hào)里面,畢竟絕大多數(shù)情況下,我們?cè)趯?xiě)代碼時(shí)一個(gè)文件里確實(shí)只會(huì)寫(xiě)一個(gè) namespace,這樣可以減少一層嵌套也是很不錯(cuò)的:

Copy

namespace MyProject;

class MyClass

{

? ? // ...

}

如果采用這樣的寫(xiě)法,每一個(gè)文件將只能聲明一個(gè) namespace。

Constant Interpolated String(100%)#

顧名思義,常量字符串插值:

Copy

const string a = "foo";

const string b = $"{a}_bar"; // foo_bar

常量字符串插值將在編譯時(shí)完成。

Lambda Improvements(100%)#

C# 10 大幅度改進(jìn)了 lambda,擴(kuò)展了使用場(chǎng)景,并改進(jìn)了一系列的推導(dǎo),提出自然委托類型,還函數(shù)上升至 first-class。

支持 Attributes#

Copy

f = [Foo] (x) => x; // 給 lambda 設(shè)置

f = [return: Foo] (x) => x; // 給 lambda 返回值設(shè)置

f = ([Foo] x) => x; // 給 lambda 參數(shù)設(shè)置

支持顯示指定返回值類型#

此前 C# 的 lambda 返回值類型靠推導(dǎo),C# 10 開(kāi)始允許在參數(shù)列表最前面顯示指定 lambda 類型了:

Copy

f = int () => 4;

支持 ref 等修飾#

Copy

f = ref int (ref int x) => ref x; // 返回一個(gè)參數(shù)的引用

First-class Functions#

方法可以被隱式轉(zhuǎn)換到 Delegate,使得函數(shù)上升至 first-class。

Copy

Delegate f = 1.GetHashCode; // Func<int>

object g = 2.ToString; // object(Func<string>)

var s = (int x) => x; // Func<int, int>

將函數(shù)作為變量,然后傳給另一個(gè)函數(shù)的參數(shù):

Copy

void Foo(Func<int> f)

{

? ? Console.WriteLine(f());

}

int Bar()

{

? ? return 5;

}

var baz = Bar;

Foo(baz);

Natural Delegate Types#

lambda 現(xiàn)在會(huì)自動(dòng)創(chuàng)建自然委托類型。

可以用 var 來(lái)創(chuàng)建委托了:

Copy

var f = () => 1; // Func<int>

var g = string (int x, string y) => $"{y}{x}"; // Func<int, string, string>

var g = "test".GetHashCode; // Func<int>

調(diào)用 lambdas#

得益于上述改進(jìn),創(chuàng)建的類型明確的 lambda 可以直接調(diào)用了。

Copy

var zero = ((int x) => x)(0); // 0

Caller Expression Attribute(80%)#

現(xiàn)在,CallerArgumentExpression 這個(gè) attribute 終于有用了。借助這個(gè) attribute,編譯器會(huì)自動(dòng)填充調(diào)用參數(shù)的表達(dá)式字符串,例如:

Copy

void Foo(int value, [CallerArgumentExpression("value")] string? expression = null)

{

? ? Console.WriteLine(expression + " = " + value);

}

當(dāng)你這樣調(diào)用時(shí):

Copy

Foo(4 + 5);

會(huì)輸出 4 + 5 = 9。這對(duì)測(cè)試極其有用,因?yàn)槟憧梢暂敵?assert 的原表達(dá)式了:

Copy

static void Assert(bool value, [CallerArgumentExpression("value")] string? expr = null)

{

? ? if (!value) throw new AssertFailureException(expr);

}

default 支持解構(gòu)(100%)#

default 現(xiàn)在支持解構(gòu)了,因此可以給 tuples 直接賦值。

Copy

(int a, int b, int c) = default; // (0, 0, 0)

List Patterns(100%)#

Pattern Matching 的最后一塊版圖:list patterns,終于補(bǔ)齊了。

Copy

void Foo(List<int> list)

{

? ? switch (list)

? ? {

? ? ? ? case [4]:

? ? ? ? ? ? Console.WriteLine("長(zhǎng)度為 4");

? ? ? ? ? ? break;

? ? ? ? case { 1, 2, 3 }:

? ? ? ? ? ? Console.WriteLine("元素是 1, 2, 3");

? ? ? ? ? ? break;

? ? ? ? case { 1, 2, ..var x, 5 }:

? ? ? ? ? ? Console.WriteLine($"前兩個(gè)元素是 1, 2,最后一個(gè)元素是 5,倒數(shù)第二個(gè)元素是 {x}");

? ? ? ? ? ? break;

? ? ? ? default:

? ? ? ? ? ? Console.WriteLine("其他");

? ? }

}

同樣的,該 pattern 也是 recursive 的,因此你可以嵌套其他 patterns。

除了上述 switch statements 的用法,在 if 以及 switch expressions 等地方也同樣可用,例如:

Copy

void Foo(List<int> list)

{

? ? var result = list switch

? ? {

? ? ? ? [4] => ...,

? ? ? ? { 1, 2, 3 } => ...,

? ? ? ? { 1, 2, ..var x, 5 } => ...,

? ? ? ? _ => ...

? ? };

}

Abstract Static Member in Interfaces(100%)#

C# 10 中,接口可以聲明抽象靜態(tài)成員了,.NET 的類型系統(tǒng)正式具備 virtual static dispatch 能力。

例如,你想定義一個(gè)可加而且有零的接口 IMonoid:

Copy

interface IMonoid<T> where T : IMonoid<T>

{

? ? abstract static T Zero { get; }

? ? abstract static T operator+(T l, T r);

}

然后可以對(duì)其進(jìn)行實(shí)現(xiàn),例如這里的 MyInt:

Copy

public class MyInt : IMonoid<MyInt>

{

? ? public MyInt(int val) { Value = val; }


? ? public static MyInt Zero { get; } = new MyInt(0);

? ? public static MyInt operator+(MyInt l, MyInt r) => new MyInt(l.Value + r.Value);


? ? public int Value { get; }

}

然后就能寫(xiě)出一個(gè)方法對(duì) IMoniod<T> 進(jìn)行求和了,這里為了方便寫(xiě)成擴(kuò)展方法:

Copy

public static class IMonoidExtensions

{

? ? public static T Sum<T>(this IEnumerable<T> t) where T : IMonoid<T>

? ? {

? ? ? ? var result = T.Zero;

? ? ? ? foreach (var i in t) result += i;

? ? ? ? return result;

? ? }

}

最后調(diào)用:

Copy

List<MyInt> list = new() { new(1), new(2), new(3) };

Console.WriteLine(list.Sum().Value); // 6

這個(gè)特性同樣也會(huì)對(duì) .NET BCL 做出改進(jìn),會(huì)新增諸如 IAddable<T>、INumeric<T> 的接口,并為適用的已有類型實(shí)現(xiàn)。

總結(jié)#

以上就是在 C# 10 的大部分新特性介紹了,雖然不保證最終效果和本文效果一致,但是也能看到一個(gè)大概的方向。

從 interface 的改進(jìn)上我們可以看到一個(gè)好的預(yù)兆:.NET 終于開(kāi)始動(dòng)類型系統(tǒng)了。2008 年至今幾乎沒(méi)有變過(guò)的 CTS 顯然逐漸不能適應(yīng)語(yǔ)言發(fā)展的需要,而 .NET 團(tuán)隊(duì)也明確給出了信息表明要在 C# 11 前后對(duì)類型系統(tǒng)集中進(jìn)行改進(jìn),現(xiàn)在只是一個(gè)開(kāi)始,相信不久之后也將能看到 traits、union types、bottom types 和 HKT 等的實(shí)裝。

USB Microphone https://www.soft-voice.com/

Wooden Speakers? https://www.zeshuiplatform.com/

亞馬遜測(cè)評(píng) www.yisuping.cn

深圳網(wǎng)站建設(shè)www.sz886.com

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容