C# 10 完整特性介绍

前言

开头防杠:.NET 的基础库、语言、运行时团队从来都是相互独立各自更新的,.NET 6 在基础库、运行时上同样做了非常多的改进,不过本文仅仅介绍语言部分。

距离上次介绍 C# 10 的特性已经有一段时间了,伴随着 .NET 6 的开发进入尾声,C# 10 最终的特性也终于敲定了。总的来说 C# 10 的更新内容很多,并且对类型系统做了不小的改动,解决了非常多现有的痛点。

从 C# 10 可以看到一个消息,那就是 C# 语言团队开始主要着重于改进类型系统和功能性方面的东西,而不是像以前那样热衷于各种语法糖了。C# 10 只是这个旅程的开头,后面的 C# 11 、12 将会有更多关于类型系统的改进,使其拥有强如 Haskell 、Rust 的表达能力,不仅能提供从头到尾的跨程序集的静态类型支持,还能做到像动态类型语言那样的灵活。逻辑代码是类型的证明,只有类型系统强大了,代码编写起来才能更顺畅、更不容易出错。

record struct

首先自然是 record struct,解决了 record 只能给 class 而不能给 struct 用的问题:

record struct Point(int X, int Y);

用 record 定义 struct 的好处其实有很多,例如你无需重写 GetHashCode 和 Equals 之类的方法了。

sealed record ToString 方法

之前 record 的 ToString 是不能修饰为 sealed 的,因此如果你继承了一个 record,相应的 ToString 行为也会被改变,因此这是个虚方法。

但是现在你可以把 record 里的 ToString 方法标记成 sealed,这样你的 ToString 方法就不会被重写了。

struct 无参构造函数

一直以来 struct 不支持无参构造函数,现在支持了:

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

但是使用的时候就要注意了,因为无参构造函数的存在使得 new struct() 和 default(struct) 的语义不一样了,例如 new Foo().X == default(Foo).X 在上面这个例子中将会得出 false

匿名对象的 with

可以用 with 来根据已有的匿名对象创建新的匿名对象了:

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

这里 y.A 将会是 3 。

全局的 using

利用全局 using 可以给整个项目启用 usings,不再需要每个文件都写一份。比如你可以创建一个 Import.cs,然后里面写:

using System;
using i32 = System.Int32;

然后你整个项目都无需再 using System,并且可以用 i32 了。

文件范围的 namespace

这个比较简单,以前写 namespace 还得带一层大括号,以后如果一个文件里只有一个 namespace 的话,那直接在最上面这样写就行了:

namespace MyNamespace;

常量字符串插值

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

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

lambda 改进

这个改进可以说是非常大,我分多点介绍。

1. 支持 attributes

lambda 可以带 attribute 了:

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

2. 支持指定返回值类型

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

f = int () => 4;

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

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

4. 头等函数

函数可以隐式转换到 delegate,于是函数上升至头等函数:

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

5. 自然委托类型

lambda 现在会自动创建自然委托类型,于是不再需要写出类型了。

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

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

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

当你调用 Foo(4 + 5) 时,会输出 4 + 5 = 9。这对测试框架极其有用,因为你可以输出 assert 的原表达式了:

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 了,同时还创建了两个变量 x 和 z,分别是 1 和 3 。

接口支持抽象静态方法

这个特性将会在 .NET 6 作为 preview 特性放出,意味着默认是不启用的,需要设置 <LangVersion>preview</LangVersion> 和 <EnablePreviewFeatures>true</EnablePreviewFeatures>,然后引入一个官方的 nuget 包 System.Runtime.Experimental 来启用。

然后接口就可以声明抽象静态成员了,.NET 的类型系统正式具备虚静态方法分发能力。

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

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

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

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

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

最后调用:

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

你可能会问为什么要引入一个 System.Runtime.Experimental,因为这个包里面包含了 .NET 基础类型的改进:给所有的基础类型都实现了相应的接口,比如给数值类型都实现了 INumber<T>,给可以加的东西都实现了 IAdditionOperators<TLeft, TRight, TResult> 等等,用起来将会非常方便,比如你想写一个函数,这个函数用来把能相加的东西加起来:

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

就搞定了。

接口的静态抽象方法支持和未来 C# 将会加入的 shape 特性是相辅相成的,届时 C# 将利用 interface 和 shape 支持 Haskell 的 class、Rust 的 trait 那样的 type classes,将类型系统上升到一个新的层次。

泛型 attribute

是的你没有看错,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(...)] 来使用你自己实现的 async method builder,代替自带的 Task 或者 ValueTask 的异步方法构造器。这也有助于你自己实现零开销的异步方法。

line 指示器支持行列和范围

以前 #line 只能用来指定一个文件中的某一行,现在可以指定行列和范围了,这对写编译器和代码生成器的人非常有用:

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

嵌套属性模式匹配改进

以前在匹配嵌套属性的时候需要这么写:

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

现在只需要简单的:

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

就可以了。

改进的字符串插值

以前 C# 的字符串插值是很粗暴的 string.Format,并且对于值类型参数来说会直接装箱,对于多个参数而言还会因此而分配一个数组(比如 string.Format("{} {}", a, b) 其实是 string.Format("{} {}", new object [] { (object)a, (object)b })),这很影响性能。现在字符串插值被改进了:

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

会被编译成:

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

上面这个 DefaultInterpolatedStringHandler 也可以借助 InterpolatedStringHandler 这个 attribute 替换成你自己实现的插值处理器,来决定要怎么进行插值。借助这些可以实现接近零开销的字符串插值。

Source Generator v2

代码生成器在 C# 10 将会迎来 v2 版本,这个版本包含很多改进,包括强类型的代码构建器,以及增量编译的支持等等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/299564.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码格式

2019独角兽企业重金招聘Python工程师标准>>> 1.参考&#xff1a;JavaScript程序编码规范 转载于:https://my.oschina.net/u/1791074/blog/283578

94年出生,6篇SCI,一作发Science,你还不放下手上玩的泥巴

全世界只有3.14 % 的人关注了 青少年数学之旅 2019年9月27日&#xff0c;国际顶尖期刊《科学》&#xff08;Science&#xff09;杂志在线以全文Article的形式发表了北京航空航天大学材料科学与工程学院赵立东教授课题组在热电材料研究上取得的新进展&#xff0c;北京航空航天大…

一个问题让我直接闭门思过!!!拼多多面试必问项之List实现类:LinkedList

一、LinkedList概述 1、对于频繁的插入或删除元素的操作&#xff0c;建议使用LinkedList类&#xff0c;效率较高。 2、LinkedList是一个实现了List接口和Deque接口的双端链表。 3、LinkedList底层的链表结构使它支持高效的插入和删除操作&#xff0c;另外它实现了Deque接口&a…

Docker小白到实战之开篇概述

前言“不对啊&#xff0c;在我这运行很正常啊”&#xff0c;这句话小伙伴们在前几年应该听得很多&#xff1b;每次一到安装、部署时总有一堆问题&#xff0c;毕竟操作系统版本、软件环境、硬件资源、网络等因素在作怪&#xff0c;此时难免会导致开发小伙伴和运维哥们互相甩锅&a…

GridView导出为Excel

protected void btExcel_Click(object sender, EventArgs e) { DataSet ds new DataSet(); ds Session["myDataSet"] as DataSet; DataTable DT ds.Tables[0]; //生成将要存放结果的Excel文件的名称 string NewFileN…

设置su为不需要密码切换为root

设置su为不需要密码 如果需要对某用户su命令也不需要输入密码&#xff0c;则需要修改下列的&#xff1a;1--->如果没有wheel组 则用sudo groupadd wheel创建命令为 sudo groupadd wheel&#xff1b;2---->sudo vim /etc/group将username和root加入到wheel用户组内 如图&a…

被女朋友拉黑后,我写了个“舔狗”必备神器

全世界只有3.14 % 的人关注了 青少年数学之旅 “ 在一个阳光明媚的清晨&#xff0c;我打开窗户呼吸了一口新鲜空气。阳光灿烂&#xff0c;岁月静好&#xff0c;又是一个约女朋友出去爬山吃饭看电影的好日子。 图片来自包图网 想到女朋友的大眼睛&#xff0c;我脸上不禁洋溢起了…

涨薪关键之反射机制,引得项目经理对你的看重,加薪触手可及!!!!

前言 就比如我前几天被面试官问什么是反射&#xff1f;&#xff1f;&#xff1f; 而我的回答是&#xff01;&#xff01;&#xff01; 反射是动态语言的关键&#xff0c;反射允许程序在执行期间借助Reflection API取得任何类的内部信息&#xff0c;并能直接操作任曦对象的内…

Wave 文件(5): 获取 Wave 文件的格式信息

装载格式信息的结构有: TWaveFormat wFormatTag: Word;nChannels: Word;nSamplesPerSec: DWORD;nAvgBytesPerSec: DWORD;nBlockAlign: Word; ;TPCMWaveFormat wf: TWaveFormat;wBitsPerSample: Word; ;TWaveFormatEx wFormatTag: Word; nChannels: Word; nSa…

如何摆脱「自我否定」状态

大家好&#xff0c;我是Z哥。你最近正处于自我否定的状态吗&#xff1f;如果不是的话&#xff0c;回想一下最近的一次处于这种状态是什么时候&#xff1f;当时的感受如何&#xff1f;以及&#xff0c;最终是如何走出这个状态的&#xff1f;不着急&#xff0c;给你 1 分钟回忆一…

struct and union

[url]http://hi.baidu.com/tweigh/blog/item/5303d2ef6e2720eace1b3e9d.html[/url]1. struct的巨大作用面对一个人的大型C/C程序时&#xff0c;只看其对struct的使用情况我们就可以对其编写者的编程经验进行评估。因为一个大型的C/C程序&#xff0c;势必要 涉及一些(甚至大量)进…

编码GBK的不可映射字符

为什么80%的码农都做不了架构师&#xff1f;>>> 由于JDK是国际版的&#xff0c;在编译的时候&#xff0c;如果我们没有用-encoding参数指定我们的JAVA源程序的编码格式&#xff0c; 则javac.exe首先获得我们操作系统默认采用的编码格式&#xff0c; 也即在编译java…

低调的大神!他改变了半导体产业!史上唯一两次获得诺贝尔物理奖,却几乎被人遗忘...

全世界只有3.14 % 的人关注了青少年数学之旅两次获得诺贝尔奖的科学家&#xff0c;世界上仅有这四个人&#xff01;他们是&#xff1a;1. 居里夫人(Marie Curie,1867~1934),波兰科学家,他的丈夫叫皮埃尔居里,两人合称“居里夫妇”! 1903年,居里夫妇和亨利...2.约翰巴丁 美国物理…

TTime::FormatL详解

TTime::FormatL详解 示例&#xff1a;TTime time;Time.HomeTime();TBuf<32> timeBuf;time.FormatL(timeBuf,_L(“%F%Y/%M/%D,%H:%T:%S”)); // 2008/08/16,21:22:22详解&#xff1a;格式串分为本地相关和本地无关格式串&#xff0c;也就是说和系统的时间日期中设置的格式…

入职第一天,我接手了号称【屎山】的祖传代码,这还能卷吗???

公司各种各样的祖传代码都是令新人虎躯一震的代码&#xff0c;因为有时候你根本不知道它是干嘛的&#xff0c;甚至觉得它毫无用处&#xff0c;关键是 还绝对不能动&#xff0c;碰一段改半年&#xff0c;别问我怎么知道的。最讽刺的是&#xff0c;你可能为了修改代码&#xff0c…

设计模式之迭代器

迭代器模式介绍集合的结构迭代器模式是一种行为设计模式&#xff0c;让你能在不暴露集合底层表现形式(列表、栈、树等)的情况下遍历集合中所有的元素。迭代器模式满足了单一职责和开闭原则&#xff0c;外界的调用方也不需要知道任何一个不同的数据结构在使用上的遍历差异。迭代…

倒啤酒竟能拿到诺贝尔物理学大奖!明明是普通操作,凭什么这么强?

全世界只有3.14 % 的人关注了青少年数学之旅倒啤酒&#xff0c;是艺术&#xff0c;更是一门学问。每个初来社会报道的男孩&#xff0c;都必须掌握这个酷炫技能。它是从古自今的文化传承&#xff0c;也是一个男人成熟的标志。向快手老铁致敬然而&#xff0c;理想很丰满&#xff…

铁山靠之——HarmonyOS基础 - 1.0

HarmonyOS学习第一章 一、HarmonyOS简介1.1 安装和使用DevEco Studio1.2 环境配置1.3 项目创建1.4 运行程序1.5 基本工程目录1.5.1 工程级目录1.5.2 模块级目录1.5.3 app.json51.5.4 module.json51.5.5 main_pages.json 二、TypeScript快速入门2.1 简介2.2 基础类型2.2.1 布尔值…

vb.net2.0 Hmac-md5加密算法

2019独角兽企业重金招聘Python工程师标准>>> <summary> Hmac-md5加密算法。 </summary> <param name"APassWord">加密串(即密码)</param> <param name"ASourceStr">原始字符串</param> <returns>&l…

[转载]AXIS学习笔记

转自&#xff1a;http://blog.csdn.net/ronghao100/archive/2005/06/08/390530.aspx 系列&#xff1a;http://www.blogjava.net/RongHao/archive/2007/06/12/123644.html 前天头告诉我用SOAP WEB服务开发一个客户程序&#xff0c;用来与企业内部的ERP进行交互。晚上赶快找相关的…