C#10 新功能
C# 10.0 向 C# 语言添加了以下功能和增强功能:
- 记录结构
- 结构类型的改进
- 可使用
const
内插字符串 - 内插字符串处理程序
global using
指令- 文件范围的命名空间声明
- 扩展属性模式
- 记录类型可密封
ToString()
- 在同一解构中可同时进行赋值和声明
- 可在方法上使用
AsyncMethodBuilder
属性
1、record struct(记录结构)
C# 9 有一个新的数据类型,叫做记录(Record)。这个类型是一种特殊的引用类型,我们只需要给出一个东西的具体属性,就可以自动为这个类型生成指定的比较器(Equals 方法、比较运算符 operator == 和 operator !=、GetHashCode 方法,甚至是 ToString 方法等等)。
举个例子,我们可以这么写:
public sealed record Point(int X, int Y);
这就等价于一个类 Point,然后生成 X 和 Y 属性,以及相关的方法:
public sealed class Point
{
public int X { get; init; }
public int Y { get; init; } public override string ToString() => $"Point {{ X = {X}, Y = {Y} }}";
public override bool Equals(object? obj) => obj is Point p && p.X == X && p.Y == y;
public override int GetHashCode() => HashCode.Combine(X, Y); public static bool operator ==(Point l, Point r) => l.Equals(r);
public static bool operator !=(Point l, Point r) => !(l == r);
}
这样的东西。你看看,就写一句话就能生成一系列的内容,是不是很方便。
不过,Point 类型就俩属性,显然没有必要定义成类,因为它太轻量级了。因此,C# 10 开始允许结构记录类型。
public record struct Point(int X, int Y);
这样就好比把前文的 sealed class 改写成 struct。因此 C# 10 开始允许结构的记录类型,所以更轻量级,灵活度更高了。当然,C# 10 依然允许引用类型(类)的记录类型,你可以使用 record 或者 record class 来表示一个类的记录类型。
首先自然是 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 方法就不会被重写了。
2、struct 无参构造函数
struct S0
{
object x = null;
object y;
// ok
} struct S1
{
object x = null;
object y;
S() { } // error: field 'y' must be assigned
} struct S2
{
object x = null;
object y;
S() : this(null) { } // ok
S(object y) { this.y = y; } // ok
}
struct Foo
{
public int X;
public Foo() { X = 1; }
}
一直以来 struct 不支持无参构造函数,现在支持了:
这个例子展示了无参构造器的使用方式。显然,跟引用类型是基本一样的,只是多了一点:如果你不定义的话,默认会生成一个无参构造器(S0 结构,y 会被自动赋值默认数值 default(object),也就是 null);但一经定义,就必须给所有没有赋值的字段和属性赋值。
当然,此时的构造器既然允许自定义了,这样就使得构造器可以定义和修改访问修饰符了。如果访问修饰符设置为 private 的话,那么外部就无法使用 new 来实例化该类型的对象了。这一点和引用类型还是一样的。
但是使用的时候就要注意了,因为无参构造函数的存在使得 new struct()
和 default(struct)
的语义不一样了,例如 new Foo().X == default(Foo).X
在上面这个例子中将会得出 false
。
2.1、结构的字段初始化器
可以看到,这个特性一旦出现,就相当于诞生了另外一个特性:结构的字段初始化器。C# 早期同样是不允许你给字段设置默认数值的;相反,你必须在构造器里赋值,还不能是默认的无参构造器里。
2.2、default(T) 表达式
另外,由于值类型和引用类型的默认数值不同的关系,定义了无参构造器必然会影响到它的默认数值
default(T) 表达式。实际上真的是这样吗?并不是。还是拿 Point
类型举例。即使你给出了默认构造器的调用,default(Point) 依旧还是原始数据的原始数据类型的默认数值构造成的实例的结果。
readonly struct Point
{
public Point() { X = int.MinValue; Y = int.MinValue; } public int X { get; }
public int Y { get; }
}
那么,default(Point) 还是 Point { X = 0, Y = 0 },而不是 Point { X = -2147483647, Y = -2147483647 } 的这个新结果。
我们把“原始数据的原始数据类型的默认数值构造成的实例”叫做零初始化实例(Zeroed
Instance),那么,default(T)
的定义就可以缩减为“该类型的零初始化实例”;换句话说,该类型的零初始化实例就是这个值类型的默认数值。那么,使用 default
表达式的时候,就算你定义了无参构造器,编译器也会始终忽略它。
3、常量字符串插值
C# 6 诞生的内插字符串并不能直接当成常量使用。举个例子:
const string A = "Sunnie";
const string B = $"Hello, {A}"; // Here
这个写法对吗?从道理上是说得通的。但是,这个写法不对。因为内插字符串没有得到编译器认为其是常量的允许,因此 B 这里会产生编译器错误。所以,你不得不改成加法运算
const string B = "Hello, " + A; // OK.
C# 10 开始允许这一点,内插字符串也可以认为是常量了;当然前提是,内插的部分也都得是常量才行;而且内插的对象必须也得是 string 才行。
4、内插字符串处理程序
C# 10 增加了对自定义内插字符串处理程序的支持。 内插字符串处理程序是处理内插字符串中的占位符表达式的一种类型。 如果没有自定义处理程序,占位符的处理方式与 String.Format 类似。 每个占位符的格式设置为文本,然后将各组成部分串联起来,形成最终的字符串。
详细请看:https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/tutorials/interpolated-string-handler
5、全局的 using
可将 global
修饰符添加到任何 using 指令,以指示编译器该指令适用于编译中的所有源文件。 这通常是项目中的所有源文件。
global using IntegerList = System.Collections.Generic<int>;
那么,整个项目就都可以使用这个 IntegerList 了。
6、命名空间
C# 10 开始你将能够在文件顶部指定该文件的 namespace,一个源文件只能包含一个namespace MyProject;声明。而不需要写一个 namespace 然后把其他代码都嵌套在大括号里面,毕竟绝大多数情况下,我们在写代码时一个文件里确实只会写一个 namespace,这样可以减少一层嵌套也是很不错的。
但是,原始的写法是允许嵌套命名空间和并排命名空间的,而一旦使用 namespace 指令后,就不可在文件里多次声明嵌套或并行的命名空间了。
namespace MyProject; class MyClass
{
// ...
}
7、元组表达式里内联变量定义
这句话不好理解。举个例子。
int age;
(name, age) = student;
假如,name 是本身就有的东西(它可能是属性,或者是字段,或者是临时变量),而 age 仅仅是一个临时变量。如果你要把右侧的 student 变量解构了的话,由于 age 的定义变量语句无法写到赋值语句里面去,所以只能分开成两行书写。
C# 10 将允许你内嵌定义语句到值元组赋值的语句里去。
(name, int age) = student;
这样就合二为一了。name 照旧赋值,而 age 则完成了定义变量和赋值两个操作,且都在这一行里就可以完成。方便了不少。
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 。
8、C#新写法,默认文件内容都是写在Main函数中,C#支持本地函数。所以里面文件中函数其实是本地函数
Console.WriteLine("该程序接收多个参数 用空格 来做参数分隔符");
Console.WriteLine($"main函数参数agrs 的长度{args.Length} ");
int i = 1;
foreach (var s1 in args)
{
Console.WriteLine($"参数{i++}:{s1}"); } Console.WriteLine(args);
Console.ReadKey();
Count();
void Count()
{ Console.WriteLine("sdfsdf"); } void Count1(string ser)
{ Console.WriteLine("sdfsdf"); }
通过IL反编译后结果
C#10 新功能的更多相关文章
- What's new in Windows 10 Enterprise with Microsoft Edge.(Windows 10 新功能)
What's new in Windows 10 Enterprise with Microsoft Edge --带有Edge浏览器的Windows 10 企业版的新功能 本文摘录自公司群发邮件, ...
- Windows 10新功能
Windows 10 中面向开发人员的新增功能 Windows 10 及新增的开发人员工具将提供新通用 Windows 平台支持的工具.功能和体验.在 Windows 10 上安装完工具和 SDK后, ...
- Windows 10 新功能
一.与 Cortana 集成的便笺 借助便笺,你可捕捉并保存绝妙创意或记录重要细节.便笺现已与 Cortana 集成,让你能够设置整个设备中的提醒. (一) 先来了解一下微软小娜Cortana. ...
- .NET6系列:C#10新功能预览
系列目录 [已更新最新开发文章,点击查看详细] 2021年4月19日微软发布公告称将于今年夏季发布首款64位的 Visual Studio 2022,2021年5月20日又发布了 Visual ...
- Java 17 将要发布,补一下 Java 13 中的新功能
本文章属于Java 新特性教程 系列,已经收录在 Github.com/niumoo/JavaNotes ,点个赞,不迷路. 自从 Oracle 调整了 Java 的版本发布节奏之后,Java 版本发 ...
- iOS 10正式发布:十大新功能,更注重人性化
6月14日凌晨消息,苹果公司举行2016年WWDC全球开发者大会,介绍了watch OS.tv OS.OS X以及iOS 10系统的新特性. 据苹果介绍,iOS 10在锁屏.Siri.地图等十个各方面 ...
- Win 10更新版1709有哪些新功能值得关注!
windows 10秋季创意者更新版1709发布已经有段时间了,也有很多用户选择升级这次更新的系统.那么,这次Win 10 更新版1709有哪些新功能值得关注呢?下面,一起随主机吧来看一看吧! 1. ...
- RAD Studio 10.3.2七大新功能介绍
RAD Studio 10.3.2七大新功能 Delphi支持macOS 64位应用的开发. C++Builder中Windows 64位平台支持C ++ 17特性. C ++ LSP 代码洞察改进. ...
- Windows 10 20H1 2004新功能
Windows 10的年度更新版本20H1即将问世.目前可以从insider preview渠道中获得.这个版本中看上去对搜索功能做了不小的改进.包括搜索的磁盘占用率以及搜索的一些展示方式. 其它的一 ...
随机推荐
- unity3d之public变量引发错误
public变量引发错误 在vs ide中怎么更改也无效 后来发现public里面的值一直不改变,手动改之.
- 全网最全的Java SpringBoot点赞功能实现
前言 最近公司在做一个NFT商城的项目,大致就是一个只买卖数字产品的平台,项目中有个需求是用户可以给商品点赞,还需要获取商品的点赞总数,类似下图 起初感觉这功能很好实现,无非就是加个点赞表嘛,后来发现 ...
- 开源办公套件DzzOffice安装教程
DzzOffice开源办公套件 DzzOffice是一套开源办公套件,适用于企业.团队搭建自己的 类似"Google企业应用套件"."微软Office365"的 ...
- fluentd分布式日志管理系统
如何有效地收集和管理大量服务器的日志一直是企业很头疼的一个问题,部分企业应用shell脚本来管理,部分企业基于hadoop来开发自己的日志管理系统,第一种管理成本巨大,需要大量的人力来维护脚本的正常运 ...
- 007 Linux 命令三剑客之-awk
01 一起来认识 awk! Linux 命令三剑客,sed.grep.awk. sed:擅长数据修改. grep:擅长数据查找定位. awk:擅长数据切片,数据格式化,功能最复杂. awk 更适合格式 ...
- Vue之性能调优
打包优化 1. 屏蔽 sourceMap sourceMap作用:项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错. 在config目录的index.js ...
- Luogu P1438无聊的数列
洛谷 P1438无聊的数列 题目链接 点这里! 题目描述 维护一个数列\(a_i\),支持两种操作: 给出一个长度等于 \(r-l+1\)的等差数列,首项为\(k\) 公差为\(d\) 并将它对应加到 ...
- PyTorch 介绍 | AUTOMATIC DIFFERENTIATION WITH TORCH.AUTOGRAD
训练神经网络时,最常用的算法就是反向传播.在该算法中,参数(模型权重)会根据损失函数关于对应参数的梯度进行调整. 为了计算这些梯度,PyTorch内置了名为 torch.autograd 的微分引擎. ...
- 《手把手教你》系列技巧篇(六十四)-java+ selenium自动化测试 - cookie -中篇(详细教程)
1.简介 今天按照原计划宏哥要用实例来给小伙伴或童鞋们来演示一下,如何利用cookie实现跳过验证码进行登录.这个场景是自动登陆.有很多系统的登陆信息都是保存在cookie里的,因此只要往cookie ...
- Windows 7 Ubuntu 修改系统启动加载项
由于现在硬盘越来越大,越来越廉价.所以越来越多的很为了方便工作学习,在一台物理机上安装多个操作系统. 下面我们就来介绍安装多个操作系统后,每次开机后,到底默认引导哪个系统,由谁说的算? 由引导项说的算 ...