一探即将到来的 C# 10

本来因为懒不想写这篇文章,但是不少人表示有兴趣,于是最后决定还是写一下。

.NET 6 最近几个预览版一直都在开发体验(如 hot reload、linker 等)、平台支持(如 Android、iOS 等)、工具链(如 crossgen2、PGO 工具和 wasm 的 AOT 等)、JIT(如 LSRA、Jump threading、PGO 和 guarded devirtualization 以及使 struct 保持在寄存器上等)、GC(如 Regions 等)以及 BCL(如 TimeOnly、DateOnly 以及 Json DOM 等)方面做改进,然而却一直没有公布 C# 10 的任何内容,即使在 Build 2021 大会上也没有提及这方面内容。然而实际上不少特性的实现已经接近尾声了,那么让我们提前来看看 C# 10 可以为我们带来什么东西。

当然,不是所有下面列出的特性都一定会进入 C# 10,也可能会和本文有所出入,我在每一个特性后面加了一个百分比表示最终实装的可能性,仅供参考。

Backing Fields(60%)#

相信不少人在编写属性的时候,因为自动属性不能满足自己的需求于是不得不改回手动实现属性,这个时候总是会想“如果能不用手动写字段的定义就好了”,现在这个梦想成真了:

Copy

private int myInt;

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

C# 10 中新增了一个 field,当使用它时会自动为属性创建字段定义,不需要再手动定义字段了,因此也叫做半自动属性。

Copy

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

Record Structs(100%)#

Records 此前只支持 class,但是现在同样支持 struct 啦,于是你可以定义值类型的 record,避免不必要的堆内存分配:

Copy

record struct Point(int X, int Y);

with on Anonymous Objects(80%)#

此前 with 只能配合 records 使用,但是现在它被扩展到了匿名对象上,你可以通过 with 来创建匿名对象的副本并且修改它的值啦:

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 语句的生效范围是单个文件的,如果你想使用一些 namespace,或者定义一系列的类型别名在整个项目内使用,那么你就需要这样:

Copy

using System.Linq;

using static System.Math;

using i32 = System.Int32;

using i64 = System.Int64;

然后在每个文件中重复一遍。但是现在不需要了,你可以定义全局的 using 了:

Copy

global using System.Linq;

global using static System.Math;

global using i32 = System.Int32;

global using i64 = System.Int64;

然后在整个项目中就都可以用了。

File Scoped Namespace(90%)#

C# 10 开始你将能够在文件顶部指定该文件的 namespace,而不需要写一个 namespace 然后把其他代码都嵌套在大括号里面,毕竟绝大多数情况下,我们在写代码时一个文件里确实只会写一个 namespace,这样可以减少一层嵌套也是很不错的:

Copy

namespace MyProject;

class MyClass

{

    // ...

}

如果采用这样的写法,每一个文件将只能声明一个 namespace。

Constant Interpolated String(100%)#

顾名思义,常量字符串插值:

Copy

const string a = "foo";

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

常量字符串插值将在编译时完成。

Lambda Improvements(100%)#

C# 10 大幅度改进了 lambda,扩展了使用场景,并改进了一系列的推导,提出自然委托类型,还函数上升至 first-class。

支持 Attributes#

Copy

f = [Foo] (x) => x; // 给 lambda 设置

f = [return: Foo] (x) => x; // 给 lambda 返回值设置

f = ([Foo] x) => x; // 给 lambda 参数设置

支持显示指定返回值类型#

此前 C# 的 lambda 返回值类型靠推导,C# 10 开始允许在参数列表最前面显示指定 lambda 类型了:

Copy

f = int () => 4;

支持 ref 等修饰#

Copy

f = ref int (ref int x) => ref x; // 返回一个参数的引用

First-class Functions#

方法可以被隐式转换到 Delegate,使得函数上升至 first-class。

Copy

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

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

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

将函数作为变量,然后传给另一个函数的参数:

Copy

void Foo(Func<int> f)

{

    Console.WriteLine(f());

}

int Bar()

{

    return 5;

}

var baz = Bar;

Foo(baz);

Natural Delegate Types#

lambda 现在会自动创建自然委托类型。

可以用 var 来创建委托了:

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>

调用 lambdas#

得益于上述改进,创建的类型明确的 lambda 可以直接调用了。

Copy

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

Caller Expression Attribute(80%)#

现在,CallerArgumentExpression 这个 attribute 终于有用了。借助这个 attribute,编译器会自动填充调用参数的表达式字符串,例如:

Copy

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

{

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

}

当你这样调用时:

Copy

Foo(4 + 5);

会输出 4 + 5 = 9。这对测试极其有用,因为你可以输出 assert 的原表达式了:

Copy

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

{

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

}

default 支持解构(100%)#

default 现在支持解构了,因此可以给 tuples 直接赋值。

Copy

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

List Patterns(100%)#

Pattern Matching 的最后一块版图:list patterns,终于补齐了。

Copy

void Foo(List<int> list)

{

    switch (list)

    {

        case [4]:

            Console.WriteLine("长度为 4");

            break;

        case { 1, 2, 3 }:

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

            break;

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

            Console.WriteLine($"前两个元素是 1, 2,最后一个元素是 5,倒数第二个元素是 {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 中,接口可以声明抽象静态成员了,.NET 的类型系统正式具备 virtual static dispatch 能力。

例如,你想定义一个可加而且有零的接口 IMonoid:

Copy

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

{

    abstract static T Zero { get; }

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

}

然后可以对其进行实现,例如这里的 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; }

}

然后就能写出一个方法对 IMoniod<T> 进行求和了,这里为了方便写成扩展方法:

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;

    }

}

最后调用:

Copy

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

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

这个特性同样也会对 .NET BCL 做出改进,会新增诸如 IAddable<T>、INumeric<T> 的接口,并为适用的已有类型实现。

总结#

以上就是在 C# 10 的大部分新特性介绍了,虽然不保证最终效果和本文效果一致,但是也能看到一个大概的方向。

从 interface 的改进上我们可以看到一个好的预兆:.NET 终于开始动类型系统了。2008 年至今几乎没有变过的 CTS 显然逐渐不能适应语言发展的需要,而 .NET 团队也明确给出了信息表明要在 C# 11 前后对类型系统集中进行改进,现在只是一个开始,相信不久之后也将能看到 traits、union types、bottom types 和 HKT 等的实装。

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

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

亚马逊测评 www.yisuping.cn

深圳网站建设www.sz886.com

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容