C# 10 新功能 -- 結構

自 C# 7 以來,結構一直都有一些新的延伸,C# 10 也沒有缺席。

parameterless struct constructors

過往結構是不能讓開發者自行顯式定義無參數建構式的,在 C#  10 則解除了這個限制。不過還是有一些要注意的地方,如果自行定義了結構的無參數建構式所有的欄位或自動實作屬性必須要在建構式內初始化 (註) – 若該成員使用了欄位或屬性初始化設定式則不在此限 。

舉個例子來說,如果設計一個結構有以下的欄位與屬性:

struct OtherStructure
{
    public int _age;
    public int Age
    {
        get { return _age; }
        set { _age = value; }
    }
    
    public String? Name { get; set; }
}

那麼它的建構是應該是這麼寫的:

 public OtherStructure()
 {
     _age = 0;
     Name = null;
 }

有一點要注意的,因為 Age 屬性並非自動實作屬性,所以下方的寫法是行不通的:

 public OtherStructure()
 {
     //無法在初始化 _age 欄位前就使用 Age 屬性
     Age = 0;
     Name = null;
 }

註:這個限制其實是自古以來對於結構所有建構式的限制,不僅僅對於無參數而已。

fields / properties initializer

解除了顯式無參數建構式限制的同時,也解除了結構不可使用欄位/屬性初始化設定式的限制,例如:

struct OtherStructure
{
    public int _age = 10;
    public int Age
    {
        get { return _age; }
        set { _age = value; }
    }
    public String? Name { get; set; } = "Bill";
}

以上這兩項限制的解除會使得結構的寫法能更接近類別一些。

default expression

這邊要注意的一點是不論是使用顯式無參數建構式或是欄位/屬性初始化設定式,使用 default(T) 敘述式得到的結果會是欄位/屬性本身型別的預設值而非開發者設定的值,例如以下的結構設計:

struct OtherStructure
{
    public int _age;
    public int Age
    {
        get { return _age; }
        set { _age = value; }
    }
    public String? Name { get; set; }

    public OtherStructure()
    {
        _age = 10;
        Name = "Bill";
    }
}

而我們這樣去設定變數:

 OtherStructure other = default(OtherStructure);

得到的 other.Age 會是 0 ; other.Name 則是 null。

record structure

C# 9 帶來了一個簡潔的型別設計方式 – record (想知道 record 讓編譯器做了些什了可以參考 C# 9.0 功能預覽 (2) recordC# 9.0 功能預覽 (3) record ),在當時 record 只能會是類別,但現在也可以使用 record 宣告結構,例如:

public readonly record struct PointA(double X, double Y, double Z);

public readonly record struct PointB
{ 
    public double X { get; init; } 
    public double Y { get; init; } 
    public double Z { get; init; } 
}
with expression

with 敘述式 C# 9 時只能用在使用 record 宣告的型別身上,現在延伸到了結構和匿名型別:

var s1 = new MyStructure();         
var s3 = s1 with { Name = "ABC", Value = 100.0 };

大致上 C# 10 對於結構的增強的部分就是這一些。