本來(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