C#9.0 新特性简介

CandidateFeaturesForCSharp9

看到标题,是不是认为我把标题写错了?是的,C# 8.0还未正式发布,在官网它的最新版本还是Preview 5,通往C#9的漫长道路却已经开始.前写天收到了活跃在C#一线的BASSAM ALUGILI给我分享C# 9.0新特性,我在他文章的基础上进行翻译,希望能对大家有所帮助.

这是世界上第一篇关于C#9候选功能的文章。阅读完本文后,你将会为未来可能遇到的C# 9.0新特性做好更充分的准备。

这篇文章基于,

C# 9.0候选新特性

原生大小的数字类型

这次引入一组新类型(nint,nuint,nfloat等)'n'表示native(原生),该特性允许声明一个32位或64位的数据类型,这取决于操作系统的平台类型。

nint nativeInt = 55; take 4 bytes when I compile in 32 Bit host.  
nint nativeInt = 55; take 8 bytes when I compile in 64 Bit host with x64 compilation settings. 

xamarin中已存在类似的概念,

xamarin原生类型

Records and Pattern-based With-Expression

这个功能我等待了很长时间,Records是一种轻量级的不可变类型,它可以是方法,属性,运算符等,它允许我们进行结构的比较, 此外,默认情况下,Records属性是只读的。

Records可以是值类型或引用类型。

Example

public class Point3D(double X, double Y, double Z);  
public class Demo   
{  
 public void CreatePoint()  
 {  
 var p = new Point3D(1.0, 1.0, 1.0); 
 } 
} 

这些代码会被编译器转化如下形式.

public class Point3D  
{  
private readonly double <X>k__BackingField;  
private readonly double <Y>k__BackingField;  
private readonly double <Z>k__BackingField;  
public double X {get {return <X>k__BackingField;}}  
public double Y{get{return <Y>k__BackingField;}}  
public double Z{get{return <Z>k__BackingField;}}  
  
 public Point3D(double X, double Y, double Z)  
 {  
 <X>k__BackingField = X;  
 <Y>k__BackingField = Y;  
 <Z>k__BackingField = Z;  
 }  
  
 public bool Equals(Point3D value)  
 {  
 return X == value.X && Y == value.Y && Z == value.Z;  
 }  
   
 public override bool Equals(object value)  
 {  
 Point3D value2;  
 return (value2 = (value as Point3D)) != null && Equals(value2);  
 }  
  
 public override int GetHashCode()  
 {  
 return ((1717635750 * -1521134295 + EqualityComparer<double>.Default.GetHashCode(X)) * -1521134295 + EqualityComparer<double>.Default.GetHashCode(Y)) * -1521134295 + EqualityComparer<double>.Default.GetHashCode(Z);  
 }  
}  
   
Using Records:  
   
public class Demo  
{  
 public void CreatePoint()  
 {  
 Point3D point3D = new Point3D(1.0, 1.0, 1.0);  
 }  
} 

Records迎合了基于表达式形式编程的特性,使得我们可以这样使用它.

var newPoint3D = Point3D.With(x: 42); 

这样我们创建的新Point(new Point3D)就像现有的一个(point3D)一样并把X的值更改为42。

这个特性于基于pattern matching也非常有效,我会在我的下一篇文章中介绍这一点.

那么我们为什么要使用Records而不是用结构体呢?为了回答这些问题,我引用了了Reddit的一句话:

“结构体是你必须要有一些约定来实现的东西。你不必手动地去让它只读,你也不用去实现他们的比较逻辑,但如果你不这样做,那你就失去了使用结构体的意义,编译器不会强制执行这些约束"。

Records类型由是编译器实现,这意味着您必须满足所有这些条件并且不能错误, 因此,它们不仅可以减少重复代码,还可以消除一大堆潜在的错误。

此外,这个功能在F#中存在了十多年,其他语言如(Scala,Kotlin)也有类似的概念。

F#

type Greeter(name: string) = member this.SayHi() = printfn "Hi, %s" name 

Scala

class Greeter(name: String)   
{  
  def SayHi() = println("Hi, " + name)  
} 

Kotlin

class Greeter(val name: String)   
{  
 fun sayhi()   
 {  
 println("Hi, ${name}");  
 }  
}

在没有Records之前,我们要实现类似的功能,C#代码要这么写

C#

public class Greeter
{
 private readonly string _name;
 public Greeter(string name)
 {
 _name = name;
 }
 public void Greet()
 {
 Console.WriteLine($ "Hello, {_name}");
 }
}

C#9.0 新特性简介

扫一扫手机访问