日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > C# >内容正文

C#

C# 10 完整特性介绍

發(fā)布時(shí)間:2023/12/4 C# 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C# 10 完整特性介绍 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

開頭防杠:.NET 的基礎(chǔ)庫、語言、運(yùn)行時(shí)團(tuán)隊(duì)從來都是相互獨(dú)立各自更新的,.NET 6 在基礎(chǔ)庫、運(yùn)行時(shí)上同樣做了非常多的改進(jìn),不過本文僅僅介紹語言部分。

距離上次介紹 C# 10 的特性已經(jīng)有一段時(shí)間了,伴隨著 .NET 6 的開發(fā)進(jìn)入尾聲,C# 10 最終的特性也終于敲定了。總的來說 C# 10 的更新內(nèi)容很多,并且對類型系統(tǒng)做了不小的改動(dòng),解決了非常多現(xiàn)有的痛點(diǎn)。

從 C# 10 可以看到一個(gè)消息,那就是 C# 語言團(tuán)隊(duì)開始主要著重于改進(jìn)類型系統(tǒng)和功能性方面的東西,而不是像以前那樣熱衷于各種語法糖了。C# 10 只是這個(gè)旅程的開頭,后面的 C# 11 、12 將會(huì)有更多關(guān)于類型系統(tǒng)的改進(jìn),使其擁有強(qiáng)如 Haskell 、Rust 的表達(dá)能力,不僅能提供從頭到尾的跨程序集的靜態(tài)類型支持,還能做到像動(dòng)態(tài)類型語言那樣的靈活。邏輯代碼是類型的證明,只有類型系統(tǒng)強(qiáng)大了,代碼編寫起來才能更順暢、更不容易出錯(cuò)。

record struct

首先自然是 record struct,解決了 record 只能給 class 而不能給 struct 用的問題:

record struct Point(int X, int Y);

用 record 定義 struct 的好處其實(shí)有很多,例如你無需重寫?GetHashCode?和?Equals?之類的方法了。

sealed record?ToString?方法

之前 record 的 ToString 是不能修飾為?sealed?的,因此如果你繼承了一個(gè) record,相應(yīng)的 ToString 行為也會(huì)被改變,因此這是個(gè)虛方法。

但是現(xiàn)在你可以把 record 里的 ToString 方法標(biāo)記成?sealed,這樣你的?ToString?方法就不會(huì)被重寫了。

struct 無參構(gòu)造函數(shù)

一直以來 struct 不支持無參構(gòu)造函數(shù),現(xiàn)在支持了:

struct Foo {public int X;public Foo() { X = 1; } }

但是使用的時(shí)候就要注意了,因?yàn)闊o參構(gòu)造函數(shù)的存在使得?new struct()?和?default(struct)?的語義不一樣了,例如?new Foo().X == default(Foo).X?在上面這個(gè)例子中將會(huì)得出?false。

匿名對象的 with

可以用 with 來根據(jù)已有的匿名對象創(chuàng)建新的匿名對象了:

var x = new { A = 1, B = 2 }; var y = x with { A = 3 };

這里?y.A?將會(huì)是 3 。

全局的 using

利用全局 using 可以給整個(gè)項(xiàng)目啟用 usings,不再需要每個(gè)文件都寫一份。比如你可以創(chuàng)建一個(gè) Import.cs,然后里面寫:

using System; using i32 = System.Int32;

然后你整個(gè)項(xiàng)目都無需再?using System,并且可以用?i32?了。

文件范圍的 namespace

這個(gè)比較簡單,以前寫 namespace 還得帶一層大括號(hào),以后如果一個(gè)文件里只有一個(gè) namespace 的話,那直接在最上面這樣寫就行了:

namespace MyNamespace;

常量字符串插值

你可以給 const string 使用字符串插值了,非常方便:

const string x = "hello"; const string y = $"{x}, world!";

lambda 改進(jìn)

這個(gè)改進(jìn)可以說是非常大,我分多點(diǎn)介紹。

1. 支持 attributes

lambda 可以帶 attribute 了:

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

2. 支持指定返回值類型

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

f = int () => 4;

3. 支持 ref 、in 、out 等修飾

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

4. 頭等函數(shù)

函數(shù)可以隱式轉(zhuǎn)換到 delegate,于是函數(shù)上升至頭等函數(shù):

void Foo() { Console.WriteLine("hello"); } var x = Foo; x(); // hello

5. 自然委托類型

lambda 現(xiàn)在會(huì)自動(dòng)創(chuàng)建自然委托類型,于是不再需要寫出類型了。

var f = () => 1; // Func<int> var g = string (int x, string y) => $"{y}{x}"; // Func<int, string, string> var h = "test".GetHashCode; // Func<int>

CallerArgumentExpression

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

void Foo(int value, [CallerArgumentExpression("value")] string? expression = null) {Console.WriteLine(expression + " = " + value); }

當(dāng)你調(diào)用?Foo(4 + 5)?時(shí),會(huì)輸出?4 + 5 = 9。這對測試框架極其有用,因?yàn)槟憧梢暂敵?assert 的原表達(dá)式了:

static void Assert(bool value, [CallerArgumentExpression("value")] string? expr = null) {if (!value) throw new AssertFailureException(expr); }

tuple 支持混合定義和使用

比如:

int y = 0; (var x, y, var z) = (1, 2, 3);

于是 y 就變成 2 了,同時(shí)還創(chuàng)建了兩個(gè)變量 x 和 z,分別是 1 和 3 。

接口支持抽象靜態(tài)方法

這個(gè)特性將會(huì)在 .NET 6 作為 preview 特性放出,意味著默認(rèn)是不啟用的,需要設(shè)置?<LangVersion>preview</LangVersion>?和?<EnablePreviewFeatures>true</EnablePreviewFeatures>,然后引入一個(gè)官方的 nuget 包?System.Runtime.Experimental?來啟用。

然后接口就可以聲明抽象靜態(tài)成員了,.NET 的類型系統(tǒng)正式具備虛靜態(tài)方法分發(fā)能力。

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

interface IMonoid<T> where T : IMonoid<T> {abstract static T Zero { get; }abstract static T operator+(T l, T r); }

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

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; } }

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

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)用:

List<MyInt> list = new() { new(1), new(2), new(3) }; Console.WriteLine(list.Sum().Value); // 6

你可能會(huì)問為什么要引入一個(gè)?System.Runtime.Experimental,因?yàn)檫@個(gè)包里面包含了 .NET 基礎(chǔ)類型的改進(jìn):給所有的基礎(chǔ)類型都實(shí)現(xiàn)了相應(yīng)的接口,比如給數(shù)值類型都實(shí)現(xiàn)了?INumber<T>,給可以加的東西都實(shí)現(xiàn)了?IAdditionOperators<TLeft, TRight, TResult>?等等,用起來將會(huì)非常方便,比如你想寫一個(gè)函數(shù),這個(gè)函數(shù)用來把能相加的東西加起來:

T Add<T>(T left, T right) where T : IAdditionOperators<T, T, T> {return left + right; }

就搞定了。

接口的靜態(tài)抽象方法支持和未來 C# 將會(huì)加入的 shape 特性是相輔相成的,屆時(shí) C# 將利用 interface 和 shape 支持 Haskell 的?class、Rust 的?trait?那樣的 type classes,將類型系統(tǒng)上升到一個(gè)新的層次。

泛型 attribute

是的你沒有看錯(cuò),C# 的 attributes 支持泛型了:

class TestAttribute<T> : Attribute {public T Data { get; }public TestAttribute(T data) { Data = data; } }

然后你就能這么用了:

[Test<int>(3)] [Test<float>(4.5f)] [Test<string>("hello")]

允許在方法上指定 AsyncMethodBuilder

C# 10 將允許方法上使用?[AsyncMethodBuilder(...)]?來使用你自己實(shí)現(xiàn)的 async method builder,代替自帶的?Task?或者?ValueTask?的異步方法構(gòu)造器。這也有助于你自己實(shí)現(xiàn)零開銷的異步方法。

line 指示器支持行列和范圍

以前?#line?只能用來指定一個(gè)文件中的某一行,現(xiàn)在可以指定行列和范圍了,這對寫編譯器和代碼生成器的人非常有用:

#line (startLine, startChar) - (endLine, endChar) charOffset "fileName"// 比如 #line (1, 1) - (2, 2) 3 "test.cs"

嵌套屬性模式匹配改進(jìn)

以前在匹配嵌套屬性的時(shí)候需要這么寫:

if (a is { X: { Y: { Z: 4 } } }) { ... }

現(xiàn)在只需要簡單的:

if (a is { X.Y.Z: 4 }) { ... }

就可以了。

改進(jìn)的字符串插值

以前 C# 的字符串插值是很粗暴的 string.Format,并且對于值類型參數(shù)來說會(huì)直接裝箱,對于多個(gè)參數(shù)而言還會(huì)因此而分配一個(gè)數(shù)組(比如?string.Format("{} {}", a, b)?其實(shí)是?string.Format("{} {}", new object [] { (object)a, (object)b })),這很影響性能。現(xiàn)在字符串插值被改進(jìn)了:

var x = 1; Console.WriteLine($"hello, {x}");

會(huì)被編譯成:

int x = 1; DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(7, 1); defaultInterpolatedStringHandler.AppendLiteral("hello, "); defaultInterpolatedStringHandler.AppendFormatted(x); Console.WriteLine(defaultInterpolatedStringHandler.ToStringAndClear());

上面這個(gè)?DefaultInterpolatedStringHandler?也可以借助?InterpolatedStringHandler?這個(gè) attribute 替換成你自己實(shí)現(xiàn)的插值處理器,來決定要怎么進(jìn)行插值。借助這些可以實(shí)現(xiàn)接近零開銷的字符串插值。

Source Generator v2

代碼生成器在 C# 10 將會(huì)迎來 v2 版本,這個(gè)版本包含很多改進(jìn),包括強(qiáng)類型的代碼構(gòu)建器,以及增量編譯的支持等等。

總結(jié)

以上是生活随笔為你收集整理的C# 10 完整特性介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。