C# 10 新特性說明

  • 1103
  • 0

C# 已經跟著 .NET 6 一起獲得了更新到了第十版了,這邊來介紹一下C# 10有哪些新玩意的筆記

這邊要說明一下 C# 10會跟著 Visual Studio 2022 ( 要有安裝 .NET 6 ) 一同使用喔! 但是如果是獨立安裝的也可以用 Visual Studio Code or Visual Studio for Mac使用喔!

接著就是小筆記的時間拉


record improvement

先前的時候使用 record 基本上就只需要用以下程式碼就可以

record Person
{
  public string FirstName { get; init; }
  public string LastName { get; init; }
}
但是這樣的 record 其實依然是 class ( reference type )

而這次介紹了 record struct 只需要把上方的程式碼改成如下就可以變成struct

record struct Person
{
  public string FirstName { get; init; }
  public string LastName { get; init; }
}

這樣可以把 init 這樣的修飾詞也讓 struct 也能使用到! 當然除了 class 和 struct 在記憶體上的allocation的地方不同之外最大的特性就是可不可以用 == ( 比較子 )做比較!如以下的Code

var person = new Person { FirstName = "F", LastName = "La" };
var anotherPerson = new Person { FirstName = "AnF", LastName = "AnLa" };
Console.WriteLine( person == anotherPerson ); // If not C# 10 will get the error!

Anonymous type improvement

在很早之前 C# 就已經支援匿名型別,但是這次更新讓匿名型別能夠支援其他新的語法,比如如下的語法

var person = new
{
  FirstName = "F",
  LastName = "La"
};
var another = person with { LastName = "AnLA" };

Custom parameterless constructor for struct

假設沿用上面的程式碼,Person是使用stuct來撰寫那麼以下程式碼的執行結果如果在 C# 10以前會是一樣的.

Person p1 = default;
Person p2 = new();

但是在C# 10開始可以支援讓 Struct 也能夠使用 custom parameterless constructor 如下所示

struct Person
{
  public Person()
  {
    FirstName = "LOL";
    LastName = "Oops";
  }
  
  public string FirstName { get; init; }
  public string LastName { get; init; }
}

要使用這功能的時候 建構子必須是要公開的 !這樣的話使用 new() 所產生的 p2 就不會等同於 p1! default 就是當作用預設值所以 p1的 FirstName, LastName 都會是 default 值而 string 的 default 值就是 null. 所以 C# 10要特別小心關於 struct 有沒有使用 custom parameterless constructor的功能.然後也可以改寫成如下的程式碼

struct Person
{
  public string FirstName { get; init; } = "LOL";
  public string LastName { get; init; } = "Oops";
}

這樣compiler就會幫你長一個public custom parameterlesse constructor出來!


File-scoped namespace and declaration

這個功能基本上直接看程式碼最清楚拉,如下所示

namespace Model
{
  struct Person
  {
    public string FirstName { get; init; } = "LOL";
    public string LastName { get; init; } = "Oops";
  }
}

在C# 10就可以簡單變成以下的程式碼

namespace Model;

struct Person
{
  public string FirstName { get; init; } = "LOL";
  public string LastName { get; init; } = "Oops";
}

直接把命名空間括號給拿掉拉~有的時候要對齊括號真的很讓人頭痛,雖然有很多好工具可以使用但簡化了一些括號是不錯的調整.


Global using directives

這個功能好處就是可以把很多 .cs 檔案的 using namespace 的功能讓所有的 .cs 檔案共同使用.所以可以建立個 globalusing.cs檔案( 檔案名稱不限拉,這只是我個人命名方式 ) 並放入以下程式碼

global using System;
global using System.Linq;
global using System.Threading.Tasks;

這個功能是在 project 中的所有 .cs 檔案都適用喔


Implicit using directives

這個功能也是可以讓 使用命名空間( using System; ) 這樣的寫法變得更加好用,在 csproject 中可以加入以下語法

<PropertyGroup>
	<ImplicitUsings>enable</ImplicitUsings>

</PropertyGroup>

或是使用 Visual Studio 2022的專案屬性然後,搜尋 using 就可以找到該選項.

這樣在該專案中的所有 .cs 檔案都可以不用使用屬於該專案類型內建的namespace,所以 Console app 的專案類型來說 System 開頭的命名空間都可以不需要加入到 .cs 檔案中了!但是如果有 static using 的命名空間除外.


Lamda improvement

關於 Lamda在 C# 也是很常用的語法,尤其在寫 delegate 等延伸的 class 的時候非常常使用到!以下的程式碼來看看差異吧

Func<string, int> convertToInt1 = new Func<string, int>(str => int.Parse(str));
Func<string, int> convertToInt2 = (string str) => int.Parse(str);
var convertToInt3 = (string str) => int.Parse(str); // C# 10

透過足夠的資訊可以推倒傳入值為string 輸出值為int 的lamda expression. 然後以下是個情境

Func<bool, object> choose = (bool b) => b ? 1 : "Oops";

這時候如果把 choose 的型別改寫成 var 就得要告訴 compiler 回傳值的型別,所以就可以寫成以下程式碼

var choose = object (bool b) => b ? 1 : "Oops";

甚至可以加上 C# Attribute

var choose = [MyAttribute] object (bool b) => b ? 1 : "Oops";

Type inference for lamdas and method groups

以下是使用 Method Group的程式碼,說真的應該沒有多少人會想要這樣寫吧…

Func<int> read = Console.Read;
Action<string> write = Console.Write;

然後可以改寫成

var read = Console.Read;
var write = Console.Write; // Not working
之所以Write不能用是因為Console.Write有高達16個 method overload所以 compiler 無法判斷出回傳的型別為何,但Console.Read只有一個所以可以推倒出型別!

Interpolated string performance optimization

基本上我們一般寫程式碼都會使用到 string,兒 interpolated string 也就是這個用法,請看下方程式碼

var myName = "James";
var myAge = 4;
Console.WriteLine($"Name => {myName}, Age => {myAge}";

這樣的程式碼在compiler底下還是去呼叫了stringbuilder所以如果是以下程式碼問題就慢慢大條了

var myAge = 4;
var sb = new StringBuilder();
sb.Append($"My age is {myAge}!";
已經使用 StringBuilder 的情況下又去使用 interpolated string 那就是在底下又去產生另一個 StringBuilder!

像是常見的問題點如下程式碼所示

public void DebugAsset(bool condition)
{
 Debug.Asset(true,$"{DateTime.Now} - {DoCalculate()}");
}

private string DoCalculate()
{
 // DO something
 return string.Empty;
}

這個功能是實作在compiler的進一步的優化,如果想要寫自己的優化步驟可以參考以下連結

$ - string interpolation - C# reference | Microsoft Docs

Explore string interpolation handlers | Microsoft Docs


心得

現在C# 10 的很多新的設計也是在簡化很多當初先前版本遺留的問題,像是在 Asp .NET 6 minial API 就是因為有以上新的特性才能讓整個專案的 cs 檔案的行數不到50行就能達成,當然相關的像是 Entity Framework Core 也是有受惠到這些新特性.基本上看看團隊最大的問題是否能夠透過修改成 C# 10的特性能夠讓團隊開發更加的有效率!


相關參考連結

What's new in C# 10 - YouTube

Welcome to C# 10 - .NET Blog (microsoft.com)