屬性 (C# 程式設計手冊)

屬性是提供彈性機制以讀取、寫入或計算私用欄位值的成員。 您可以將屬性當做公用資料成員來使用,但屬性是稱為「存取子」的特殊方法。 這項功能可讓資料更容易存取,同時有助於提升方法的安全性和彈性。

屬性概觀

  • 屬性可讓類別公開取得和設定值的公用方式,同時隱藏實作或驗證程式碼。
  • get 屬性存取子可用來傳回屬性值,而 set 屬性存取子則用來指派新值。 init 屬性存取子只會在物件建構期間用來指派新的值。 這些存取子可以有不同的存取層級。 如需詳細資訊,請參閱限制存取子的存取範圍
  • value 關鍵詞是用來定義 或 init 存取子指派的值set
  • 屬性可以是「讀寫」(同時具有 getset 存取子)、「唯讀」(具有 get 存取子但沒有 set 存取子) 或「唯寫」(具有 set 存取子但沒有 get 存取子)。 唯寫屬性很少見,而且最常用來限制對機密資料的存取。
  • 不需要自訂存取子程式碼的簡單屬性,則可以實作為運算式主體定義或自動實作屬性

含有支援欄位的屬性

實作屬性的一種基本模式需要使用私用支援欄位,來設定和擷取屬性值。 get 存取子會傳回私用欄位的值,而 set 存取子則可能會執行一些資料驗證,再將值指派給私用欄位。 這兩個存取子也可能會對資料執行一些轉換或計算,再儲存或傳回資料。

下列範例將示範這個模式。 在此範例中,TimePeriod 類別代表時間間隔。 就內部而言,此類別會將時間間隔 (秒) 儲存在名為 _seconds 的私用欄位中。 名為 Hours 的讀寫屬性可讓客戶以小時為單位指定時間間隔。 getset 存取子都會執行小時與秒之間的必要轉換。 此外,set 存取子會驗證資料,並在小時數無效時擲回 ArgumentOutOfRangeException

public class TimePeriod
{
    private double _seconds;

    public double Hours
    {
        get { return _seconds / 3600; }
        set
        {
            if (value < 0 || value > 24)
                throw new ArgumentOutOfRangeException(nameof(value),
                      "The valid range is between 0 and 24.");

            _seconds = value * 3600;
        }
    }
}

您可以存取屬性來取得和設定值,如下列範例所示:

TimePeriod t = new TimePeriod();
// The property assignment causes the 'set' accessor to be called.
t.Hours = 24;

// Retrieving the property causes the 'get' accessor to be called.
Console.WriteLine($"Time in hours: {t.Hours}");
// The example displays the following output:
//    Time in hours: 24

運算式主體定義

屬性存取子通常是由只會指派或傳回運算式結果的單行陳述式所組成。 您可以將這些屬性實作為運算式主體成員。 運算式主體定義包含 => 符號,後面接著要從屬性指派或擷取的運算式。

唯讀屬性可將 get 存取子實作為運算式主體成員。 在此情況下,不會使用 get 存取子關鍵字和 return 關鍵字。 下列範例會將唯讀 Name 屬性實作為運算式主體成員。

public class Person
{
    private string _firstName;
    private string _lastName;

    public Person(string first, string last)
    {
        _firstName = first;
        _lastName = last;
    }

    public string Name => $"{_firstName} {_lastName}";
}

getset 存取子都可以實作為運算式主體成員。 在此情況下,必須同時有 getset 關鍵字。 下列範例說明如何使用運算式主體定義來表示這兩個存取子。 return 關鍵字不能搭配 get 存取子使用。

public class SaleItem
{
    string _name;
    decimal _cost;

    public SaleItem(string name, decimal cost)
    {
        _name = name;
        _cost = cost;
    }

    public string Name
    {
        get => _name;
        set => _name = value;
    }

    public decimal Price
    {
        get => _cost;
        set => _cost = value;
    }
}

自動實作屬性

在某些情況下,屬性 getset 存取子只會指派值給支援欄位,或從支援欄位中擷取值,而不會包含任何額外的邏輯。 藉由使用自動實作屬性,您可以簡化程式碼,同時讓 C# 編譯器無障礙地為您提供支援欄位。

如果屬性具有 getset (或是 getinit) 存取子,這兩者都必須自動實作。 您可以使用 getset 關鍵字,但不提供任何實作,來定義自動實作屬性。 下列範例會重複上一個範例,不同之處在於 NamePrice 為自動實作屬性。 此範例也會移除參數化建構函式,因此 SaleItem 物件現在會透過呼叫無參數建構函式和物件初始設定式來初始化。

public class SaleItem
{
    public string Name
    { get; set; }

    public decimal Price
    { get; set; }
}

自動實作的屬性可以為 getset 存取子宣告不同的存取範圍。 您通常會宣告公用 get 存取子和私用 set 存取子。 您可以在限制存取子的存取範圍一文中深入了解。

必要屬性

從 C# 11 開始,您可以新增 required 成員來強制用戶端程式碼初始化任何屬性或欄位:

public class SaleItem
{
    public required string Name
    { get; set; }

    public required decimal Price
    { get; set; }
}

若要建立 SaleItem,您必須使用物件初始設定式同時設定 NamePrice 屬性,如下列程式碼所示:

var item = new SaleItem { Name = "Shoes", Price = 19.95m };
Console.WriteLine($"{item.Name}: sells for {item.Price:C2}");

C# 語言規格

如需詳細資訊,請參閱 C# 語言規格屬性。 語言規格是 C# 語法及用法的限定來源。

另請參閱