C# 12

前言

C# 12 是 .NET 8 版本推出的,目前还在 RC 阶段。预计 2023 年 11 月份就会正式推出了。

想提早玩玩的话可以使用用 Visual Studio Preview

 

参考

Docs – What's new in C# 12

 

Primary Constructors

参考:

Docs – Tutorial: Explore primary constructors

Docs – Primary constructors

YouTube – The New Constructor Type Coming in C# 12 is Weird

C# 9.0 推出的 Record 就有 Primary constructors 的概念。

它的 definition 非常短

public record Dimension(int Width, int Height);

相同的逻辑,class 就很长很繁琐。

public class Dimension {
    public int Width { get; set; }
    public int Height { get; set; }

    public Dimension(int width, int height) { 
        Width = width; 
        Height = height;
    }
}

于是,C# 12 也让 class 和 struct 有了 primary constructors 的功能。

但是要注意哦,它们有一些些微妙的不同,很容易让你掉坑。

Class primary constructors != Record primary constructors

public class Dimension(int width, int height);

上面这句和 Record 语法类似,但是效果却差很多。

首先,它的参数是 camelCase,Record 是 PascalCase,为什么呢?

因为 Record 的参数是 public property,而 class 的 parameter 是 private field。

所以,property 的写法应该是长这样的。

public class Dimension(int width, int height) {
    public int Width { get; set; } = width;
    public int Height { get; set; } = height;
}

不管怎样,它还是比之前短了一些。还是不错用的。

Parameters can be use anywhere

参数不仅仅可以用于 assign to property,它也可以在方法中使用。

public class Dimension(int width, int height) 
{
    public int Sum() {
        return width + height;
    }
}

原因上面提到了,paramters 其实是 private filed 来的

我们把代码放到 sharplab.io 查看就明白了

简单说,primary constructors 就是语法糖,它就是替你写了一个 constructor 还有 private fields.

Dependency Injection

C# 11

public class Dimension
{
    private readonly Service _service;
    public Dimension(Service service) {
        _service = service;
    }
}

C# 12

public class Dimension(Service service)
{
    private readonly Service _service = service;
}

如果你不是很在意 readonly 的话,可以省略

public class Dimension(Service service) {

}

本来听说会支持 readonly 语法

public class Dimension(readonly Service service) {

}

但最后视乎是被否决掉了。

总结

C# 12 以后,Record, Class, Struct 又又又更加像了,更傻傻分不清楚了,更容易掉坑了。

记住,class 的 primary constructors 是 constructor + private fields, Record 则是 constructor + public properties。

 

Collection Expressions

日常我们定义 collection 的方式

var numbers = new List<int> { 1, 2, 3 };

上面是 var 推导类型的 defined 写法

换成先 declare 类型是这样

List<int> numbers = new() { 1, 2, 3 };

C# 12 对上面这句语法又做了优化,现在只需要这样写

List<int> numbers = [1, 2, 3];

单纯比长度的话,本来只是少了 var 现在又少了 new 确实短了不少。var 剩下的唯一好处就只有起头字数一样,可以对齐。

它只是语法糖而已,通过 sharplab.io 查看

转译出来就是 new List + add item

Spread Operator

写过 JavaScript 的人对 spread operator 应该不陌生了。C# 12 也有了这个功能。

var numbers = new List<int> { 1, 2, 3 };
List<int> numbers2 = [..numbers, 4, 5]; // 语法正确
var number3 = new List<int> { ..numbers2, 6, 7 }; // 语法错误哦

 

Default lambda parameters

C# 11 lambda 是不支持 default parameter value 的。C# 12 以后支持了。

C# 12

如何动态创建表达式树 for default parameters 我还没有研究。以后补上。

 

Alias any type

Alias type 有点像 TypeScript 的 Type Aliases,但也不完全一样。

它的写法长这样

using Point = Coordinate;

public class Coordinate {
    public int X { get; set; }
    public int Y { get; set; }
}

它可以完全用一个 alias 别名取代任何类型,就真的是 rename 而已哦。

var point = new Point();

上面这个在 C# 是 ok 的,但在 TypeScript 是做不到的。

使用场景我还没有什么体会,目前看到的第一个好处是可以用来 define Tuple。

using Person = (string Name, int Age);

public class Service
{
  public void DoSomething(Person[] people)
  {
    var service = new Service();
    service.DoSomething([("Derrick", 1)]);
  }
}

这个应该是最短的写法了。从前至少都要使用 Record,而 Record 是需要 new () 的,上面连 new 都不需要了。

局限

目前看到 2 个局限

1. using 的位置只能在最顶部,这个不够方面。

2. alias 不可用于 alias

using Person = (string Name, int Age);
using People1 = Person[]; // Error: The type or namespace name 'Person' could not be found
using People2 = List<Person>; // Error: The type or namespace name 'Person' could not be found

上面 2 个写法都不支持。

 

posted @ 2023-09-18 14:03  兴杰  阅读(204)  评论(0编辑  收藏  举报