預覽 c# 10 的新東西

預覽 c# 10 的新東西

學習永不止步

最后更新 2021/6/1 下午10:19
Ken Bonny&Rwing
预计阅读 6 分钟
分类
.NET
标签
.NET C#

本周早些时候(译注:原文发表于 5 月 1 日),我关注了 Mads TorgersenDotNet SouthWest 大会上的演讲,他是微软的 C# 语言的首席设计师。他概述了 C# 10 即将包含的很酷的一些新东西。让我们来快速浏览一下。

小小的免責聲明,這些變化中的大部分已經基本完成。但是由於它仍在積極的開發中,我不能保證 c# 10 發布時所有東西都會完全如實。

struct record

他谈到的第一件事是,目前 record 的实现是使用一个 class 作为基础对象的。现在还会有一个 record struct 的变体可用,所以基础类型可以是一个值类型。区别在于,普通的 record 在函数之间传递的是引用,而 record struct 是其值的拷贝。 record struct 也会支持 with 运算符。

同時,還允許向 record 添加運算符,兩種 record 類型都可以。

record Person(string Name, string Email)
{
  public static Person operator +(Person first, Person second)
  {
    // logic goes here
  }
}

required

c# 團隊關注的目標之一是使對象的初始化變得更容易。這就是為什麼可以對 class、struct、record 或 struct record 的屬性添加 required 標記 。它使得這些屬性必須填寫。這可以通過構造函數來完成,也可以通過對象初始化來完成。下面的兩個類的定義是等價的。如果你添加了 required 關鍵字,那麼就無法在不設置 name 屬性的情況下將 person 實例化。編譯器會拋出錯誤,無法編譯。

class Person
{
  public required string Name { get; set; }
  public DateTime DateOfBirth { get; set; }
}
class Person
{
  public Person(string name) => Name = name;

  public string Name { get; set; }
  public DateTime DateOfBirth { get; set; }
}

field

為了進一步的改善屬性,將允許完全擺脫 backing field 。新的關鍵字 field 將提供對上述欄位的訪問。對 setter 和 init only 屬性都可以使用。

class Person
{
  public string Name { get; init => field = value.Trim(); }
  public DateTime DateOfBirth { get; set => field = value.Date; }
}

with

在下一个版本中还会有一些有趣的小改进。其实中一个是匿名类型也将支持 with 运算符。

var foo = new
{
  Name = "Foo",
  Email = "foo@mail.com"
};
var bar = foo with {Name = "Bar"};

namespace

现在可以创建一个带有命名空间导入的文件,然后在任何地方都可以使用这个导入。例如,如果有一个很常用的命名空间,几乎在每个文件中都使用例如 Microsoft.Extensions.Logging.ILogger ,那么就可以在任何.cs 文件(我建议在 Program.cs 或专门的 Imports.cs )中添加一行 global using Microsoft.Extensions.Logging.ILogger,之后这个命名空间将可以在整个项目中使用。注意,这不适用于整个解决方案! 没有人能够预测哪些地方需要导入,所以它们被分组到每个项目中。

// 译注:原文并没有提供代码示例,为了更好方便大家理解私自添加了一个演示
// Program.cs 文件
global using System;

// Sample.cs 文件
// 可以不用再using System;
Console.WriteLine("foo");

隨後,還將對命名空間也會有一個優化。現在命名空間需要大括號 來包起來代碼,這就意味著所有代碼至少要縮進一次。為了節省 tab(或四個空格)和屏幕空間,在文件的任何地方添加一個命名空間,將使所有代碼都屬於該命名空間。有研究表明絕大多數情況下,一個文件中所有的代碼都屬於同一個命名空間。使用這個方案後,文件大小隨之減少,這對一個解決方案來說可能並不明顯(即使它包含成千上萬的文件),但在 github/gitlab/bitbucket/...的規模上,我認為這將為他們節省一些空間。如果有人仍想在一個文件中包含多個命名空間,使用大括號的選項仍然可用。

// 传统的方式 LegacyNamespace.cs
namespace LegacyNamespace
{
  class Foo
  {
    // legacy code goes here
  }
}

// 简化的方式 SimplifiedNamespace.cs
namespace SimplifiedNamespace;
class Bar
{
  // awesome code goes here
}

lambda

對 lambda 語句也會有一些很酷的更新。編譯器將對推斷 lambda 簽名提供更好的支持,而且還可以添加特性。話可以顯式指定返回類型,以幫助編譯器理解 lambda。

var f = Console.WriteLine;
var f = x => x; // 推断返回类型
var f = (string x) => x; // 推断签名
var f = [NotNull] x => x; // 添加特性
var f = [NotNull] (int x) => x;
var f = [return: NotNull] static x => x; // 添加特性
var f = T () => default; // 显式返回类型
var f = ref int (ref int x) => ref x; // 使用 ref
var f = int (x) => x; // 显式指定隐式输入的返回类型
var f = static void (_) => Console.Write("Help");

感謝 schooley 提出了一個不那麼容易混淆的特性例子

interface

最後,將有可能在接口上指定靜態方法和屬性。我知道這將是一個有爭議的話題,就像給接口添加默認實現一樣。我不喜歡它。然而,這可能非常有趣。想像一下,你可以指定一個接口的默認值或指定創建方法。

interface IFoo
{
  static IFoo Empty { get; }
  static operator +(IFoo first, IFoo second);
}
class Foo : IFoo
{
  public static IFoo Empty => new Foo();
  public static operator +(IFoo first, IFoo second) => /* do calculation here */;
}

就个人而言,我喜欢这些变化。我最喜欢的是对命名空间的改变和对接口的改进。总之,未来是光明的 C# 的。嗯嗯... (译注:这里作者玩了一个梗,原文 the future is seeing sharp,see sharp 发音类似 C# )

謝謝各位,大家再見。

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2026/2/7

aot使用經驗總結

從項目創建伊始,就應養成良好的習慣,即只要添加了新功能或使用了較新的語法,就及時進行 aot 發布測試。

继续阅读