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

屬性是提供彈性機制以讀取、寫入或計算私用欄位值的成員。 使用屬性時可將其視為公用資料成員,但實際上屬性是名為「存取子」的特殊方法。 如此可讓資料更容易存取,同時有助於提升方法的安全性和彈性。

屬性概觀

  • 屬性可讓類別公開取得和設定值的公用方式,同時隱藏實作或驗證程式碼。

  • get 屬性存取子可用來傳回屬性值,而 set 屬性存取子則用來指派新值。 在 c # 9 和更新版本中,只有在物件結構期間,才會使用 init 屬性存取子來指派新值。 這些存取子可以有不同的存取層級。 如需詳細資訊,請參閱限制存取子的存取範圍

  • Value關鍵字是用來定義或存取子所指派的值 set init

  • 屬性可以是「讀寫」(同時具有 getset 存取子)、「唯讀」(具有 get 存取子但沒有 set 存取子) 或「唯寫」(具有 set 存取子但沒有 get 存取子)。 唯寫屬性很少見,而且最常用來限制對機密資料的存取。

  • 不需要自訂存取子程式碼的簡單屬性,則可以實作為運算式主體定義或自動實作屬性

含有支援欄位的屬性

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

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

using System;

class TimePeriod
{
   private double _seconds;

   public double Hours
   {
       get { return _seconds / 3600; }
       set {
          if (value < 0 || value > 24)
             throw new ArgumentOutOfRangeException(
                   $"{nameof(value)} must be between 0 and 24.");

          _seconds = value * 3600;
       }
   }
}

class Program
{
   static void Main()
   {
       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

運算式主體定義

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

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

using System;

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

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

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

public class Example
{
   public static void Main()
   {
      var person = new Person("Magnus", "Hedlund");
      Console.WriteLine(person.Name);
   }
}
// The example displays the following output:
//      Magnus Hedlund

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

using System;

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;
   }
}

class Program
{
   static void Main(string[] args)
   {
      var item = new SaleItem("Shoes", 19.95m);
      Console.WriteLine($"{item.Name}: sells for {item.Price:C2}");
   }
}
// The example displays output like the following:
//       Shoes: sells for $19.95

自動實作屬性

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

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

using System;

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

   public decimal Price
   { get; set; }
}

class Program
{
   static void Main(string[] args)
   {
      var item = new SaleItem{ Name = "Shoes", Price = 19.95m };
      Console.WriteLine($"{item.Name}: sells for {item.Price:C2}");
   }
}
// The example displays output like the following:
//       Shoes: sells for $19.95

C# 語言規格

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

另請參閱