本来因为懒不想写这篇文章,但是不少人表示有兴趣,于是最后决定还是写一下。
.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%)相信不少人在编写属性的时候,因为自动属性不能满足自己的需求于是不得不改回手动实现属性,这个时候总是会想“如果能不用手动写字段的定义就好了”,现在这个梦想成真了:
private int myInt;public int MyInt { get => myInt; set => myInt = value; }
C# 10 中新增了一个 fIEld
,当使用它时会自动为属性创建字段定义,不需要再手动定义字段了,因此也叫做半自动属性。
public int MyInt { get => fIEld; set => fIEld = value; }
Record Structs(100%)Records 此前只支持 class,但是现在同样支持 struct 啦,于是你可以定义值类型的 record,避免不必要的堆内存分配:
record struct Point(int X,int Y);
with
on Anonymous Objects(80%)此前 with
只能配合 records 使用,但是现在它被扩展到了匿名对象上,你可以通过 with
来创建匿名对象的副本并且修改它的值啦:
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,或者定义一系列的类型别名在整个项目内使用,那么你就需要这样:
using System.linq;using static System.Math;using i32 = system.int32;using i64 = system.int64;
然后在每个文件中重复一遍。但是现在不需要了,你可以定义全局的 using
了:
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,这样可以减少一层嵌套也是很不错的:
namespace MyProject;class MyClass{ // ...}
如果采用这样的写法,每一个文件将只能声明一个 namespace。
Constant Interpolated String(100%)顾名思义,常量字符串插值:
const string a = "foo";const string b = $"{a}_bar"; // foo_bar
常量字符串插值将在编译时完成。
Lambda Improvements(100%)C# 10 大幅度改进了 lambda,扩展了使用场景,并改进了一系列的推导,提出自然委托类型,还函数上升至 first-class。
支持 Attributesf = [Foo] (x) => x; // 给 lambda 设置f = [return: Foo] (x) => x; // 给 lambda 返回值设置f = ([Foo] x) => x; // 给 lambda 参数设置
支持显示指定返回值类型此前 C# 的 lambda 返回值类型靠推导,C# 10 开始允许在参数列表最前面显示指定 lambda 类型了:
f = int () => 4;
支持 ref
等修饰f = ref int (ref int x) => ref x; // 返回一个参数的引用
First-class Functions方法可以被隐式转换到 Delegate,使得函数上升至 first-class。
Delegate f = 1.GetHashCode; // Func<int>object g = 2.ToString; // object(Func<string>)var s = (int x) => x; // Func<int,int>
将函数作为变量,然后传给另一个函数的参数:
voID Foo(Func<int> f){ Console.Writeline(f());}int bar(){ return 5;}var baz = bar;Foo(baz);
Natural Delegate Typeslambda 现在会自动创建自然委托类型。
可以用 var
来创建委托了:
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 可以直接调用了。
var zero = ((int x) => x)(0); // 0
Caller Expression Attribute(80%)现在,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);}
default 支持解构(100%)default 现在支持解构了,因此可以给 tuples 直接赋值。
(int a,int b,int c) = default; // (0,0)
List Patterns(100%)Pattern Matching 的最后一块版图:List patterns,终于补齐了。
voID Foo(List<int> List){ switch (List) { case [4]: Console.Writeline("长度为 4"); break; case { 1,2,3 }: Console.Writeline("元素是 1,3"); break; case { 1,..var x,5 }: Console.Writeline($"前两个元素是 1,2,最后一个元素是 5,倒数第二个元素是 {x}"); break; default: Console.Writeline("其他"); }}
同样的,该 pattern 也是 recursive 的,因此你可以嵌套其他 patterns。
除了上述 switch statements 的用法,在 if 以及 switch Expressions 等地方也同样可用,例如:
voID Foo(List<int> List){ var result = List switch { [4] => ...,{ 1,3 } => ...,5 } => ...,_ => ... };}
Abstract Static Member in Interfaces(100%)C# 10 中,接口可以声明抽象静态成员了,.NET 的类型系统正式具备 virtual static dispatch 能力。
例如,你想定义一个可加而且有零的接口 IMonoID
:
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
这个特性同样也会对 .NET BCL 做出改进,会新增诸如 IAddable<T>
、INumeric<T>
的接口,并为适用的已有类型实现。
以上就是在 C# 10 的大部分新特性介绍了,虽然不保证最终效果和本文效果一致,但是也能看到一个大概的方向。
从 interface 的改进上我们可以看到一个好的预兆:.NET 终于开始动类型系统了。2008 年至今几乎没有变过的 CTS 显然逐渐不能适应语言发展的需要,而 .NET 团队也明确给出了信息表明要在 C# 11 前后对类型系统集中进行改进,现在只是一个开始,相信不久之后也将能看到 traits、union types、bottom types 和 HKT 等的实装。
总结以上是内存溢出为你收集整理的一探即将到来的 C# 10全部内容,希望文章能够帮你解决一探即将到来的 C# 10所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)