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

屬性 (Property) 是欄位和方法的綜合體; 如果是物件的使用者,屬性會以欄位出現,存取屬性需要相同的語法。 而對於類別的實作者,屬性則是一或兩個程式碼區塊,代表 get 存取子 (Accessor) 和 (或) set 存取子。 get 存取子的程式碼區塊會於讀取屬性時執行,而且 set 存取子的程式碼區塊會在指派新值給該屬性時執行。 不含 set 存取子的屬性會被視為唯讀, 而不含 get 存取子的屬性會被視為唯寫; 同時具有這兩種存取子的屬性則為可讀寫。

屬性並不會歸類為變數,這點與欄位不同。 因此,您無法將屬性當成 ref (C# 參考)out (C# 參考) 參數進行傳遞。

屬性能有許多用途:可以在允許變更之前驗證資料;可以在實際從其他某些來源 (例如資料庫) 擷取資料時,透明地在某一類別上公開資料;也可以在資料變更 (例如引發事件,或是變更其他欄位的值) 時採取動作。

屬性在類別區塊內宣告的方式是指定欄位存取層級、接著指定屬性的型別、再指定屬性的名稱,然後是宣告 get 存取子和 (或) set 存取子的程式碼區塊。 例如:

public class Date
{
    private int month = 7;  // Backing store

    public int Month
    {
        get
        {
            return month;
        }
        set
        {
            if ((value > 0) && (value < 13))
            {
                month = value;
            }
        }
    }
}

在此範例中,Month 被宣告為屬性,如此 set 存取子即可確保 Month 值會設定為 1 到 12 之間。 Month 屬性使用私用欄位來追蹤實際的值。 屬性資料的實際位置通常稱為屬性的「支援的存放區」,屬性經常會使用私用欄位做為支援的存放區, 而欄位之所以標記為私用的原因,是要確保一定要透過呼叫屬性才能夠變更欄位。 如需公用和私用存取限制的詳細資訊,請參閱存取修飾詞 (C# 程式設計手冊)

自動實作的屬性為簡單屬性宣告提供了簡化的語法。 如需詳細資訊,請參閱自動實作的屬性 (C# 程式設計手冊)

get 存取子

get 存取子的主體就像方法的主體。 它必須傳回屬性型別的值。 執行 get 存取子的意思就是等於讀取欄位值; 例如,當您從 get 存取子傳回私用變數,並啟用最佳化時,編譯器就會內嵌對 get 存取子方法的呼叫,如此即可避免方法呼叫額外負荷。 然而,由於在編譯時期,編譯器不知道在執行階段可以確實呼叫哪些方法,因此,無法內嵌虛擬的 get 存取子方法。 下列是一個 get 存取子,它會傳回私用欄位 name 的值:

class Person
{
    private string name;  // the name field
    public string Name    // the Name property
    {
        get
        {
            return name;
        }
    }
}

當您參考屬性時,除了指派的目標之外,還會叫用 get 存取子來讀取屬性值, 例如:

Person person = new Person();
//...

System.Console.Write(person.Name);  // the get accessor is invoked here

get 存取子必須在 returnthrow 陳述式結束,而且控制項不能超出存取子的主體。

使用 get 存取子來變更物件狀態是不好的程式設計作法, 例如,每一次存取 number 欄位時,下列存取子會造成物件狀態變更的不良結果。

private int number;
public int Number
{
    get
    {
        return number++;   // Don't do this
    }
}

get 存取子可以用於傳回欄位值,或予以計算並且傳回。 例如:

class Employee
{
    private string name;
    public string Name
    {
        get
        {
            return name != null ? name : "NA";
        }
    }
}

在之前的程式碼片段中,如果您沒有將值指派給 Name 屬性,它會傳回 NA 值。

set 存取子

set 存取子就像傳回型別為 void 的方法。 它使用稱為 value 的隱含參數,其型別是屬性的型別。 在下列範例中,會將 set 存取子加入 Name 屬性:

class Person
{
    private string name;  // the name field
    public string Name    // the Name property
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
        }
    }
}

當您將值指派給屬性,會以提供新值的引數來叫用 set 存取子。 例如:

Person person = new Person();
person.Name = "Joe";  // the set accessor is invoked here                

System.Console.Write(person.Name);  // the get accessor is invoked here

在 set 存取子中,區域變數宣告使用隱含參數名稱 value 是錯誤的。

備註

屬性可以標記為 public、private、protected、internal 或 protected internal, 這些存取修飾詞 (Modifier) 將定義類別使用者如何存取屬性。 相同屬性的 get 和 set 存取子可能具有不同的存取修飾詞。 例如,get 可能具有 public,以允許來自型別外部的唯讀存取,而 set 則可能具有 private 或 protected。 如需詳細資訊,請參閱存取修飾詞 (C# 程式設計手冊)

您可以使用 static 關鍵字,將屬性宣告為靜態屬性。 這讓呼叫端即使沒有類別的執行個體,也可以隨時使用屬性。 如需詳細資訊,請參閱靜態類別和靜態類別成員 (C# 程式設計手冊)

屬性可以使用 virtual 關鍵字標記為虛擬屬性。 如此一來,衍生類別就可以使用 override 關鍵字覆寫屬性行為。 如需這些選項的詳細資訊,請參閱 繼承 (C# 程式設計手冊)

用來覆寫虛擬屬性的屬性也可以是 sealed,對於衍生類別來說,該屬性即不再為虛擬的。 最後,屬性可以宣告為 abstract。 這表示類別中不會有實作,而衍生類別必須撰寫本身的實作。 如需這些選項的詳細資訊,請參閱 抽象和密封類別以及類別成員 (C# 程式設計手冊)

注意事項注意事項

static 屬性的存取子上,使用 virtual (C# 參考)abstract (C# 參考)override (C# 參考) 修飾詞是錯誤的。

範例

這個範例示範執行個體、靜態和唯讀屬性; 該屬性會接受從鍵盤輸入的員工名稱、以 1 遞增 NumberOfEmployees,及顯示員工名稱和編號。

public class Employee
{
    public static int NumberOfEmployees;
    private static int counter;
    private string name;

    // A read-write instance property:
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    // A read-only static property:
    public static int Counter
    {
        get { return counter; }
    }

    // A Constructor:
    public Employee()
    {
        // Calculate the employee's number:
        counter = ++counter + NumberOfEmployees;
    }
}

class TestEmployee
{
    static void Main()
    {
        Employee.NumberOfEmployees = 107;
        Employee e1 = new Employee();
        e1.Name = "Claude Vige";

        System.Console.WriteLine("Employee number: {0}", Employee.Counter);
        System.Console.WriteLine("Employee name: {0}", e1.Name);
    }
}
/* Output:
    Employee number: 108
    Employee name: Claude Vige
*/

這個範例示範如何存取基底類別中的屬性,該屬性被衍生類別中名稱相同的另一個屬性所隱藏。

public class Employee
{
    private string name;
    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}

public class Manager : Employee
{
    private string name;

    // Notice the use of the new modifier:
    public new string Name
    {
        get { return name; }
        set { name = value + ", Manager"; }
    }
}

class TestHiding
{
    static void Main()
    {
        Manager m1 = new Manager();

        // Derived class property.
        m1.Name = "John";

        // Base class property.
        ((Employee)m1).Name = "Mary";

        System.Console.WriteLine("Name in the derived class is: {0}", m1.Name);
        System.Console.WriteLine("Name in the base class is: {0}", ((Employee)m1).Name);
    }
}
/* Output:
    Name in the derived class is: John, Manager
    Name in the base class is: Mary
*/

以下是上述範例中的重點:

  • 衍生類別中的 Name 屬性會隱藏基底類別中的 Name 屬性, 在這種情況下,new 修飾詞是用於衍生類別裡的屬性宣告中:

    public new string Name
    
  • 轉換 (Employee) 是用來存取基底類別中的隱藏屬性:

    ((Employee)m1).Name = "Mary";
    

    如需隱藏成員的詳細資訊,請參閱 new 修飾詞 (C# 參考)

在這個範例裡,Cube 和 Square 兩個類別會實作 Shape 抽象類別 (Abstract Class),並且覆寫它的抽象 Area 屬性。 請注意 override 修飾詞在屬性上的用法。 該程式會接受邊長的輸入值,然後計算正方形和立方體的面積, 而且,還也會接受面積的輸入值,然後計算正方形和立方體的對應邊長。

abstract class Shape
{
    public abstract double Area
    {
        get;
        set;
    }
}

class Square : Shape
{
    public double side;

    public Square(double s)  //constructor
    {
        side = s;
    }

    public override double Area
    {
        get
        {
            return side * side;
        }
        set
        {
            side = System.Math.Sqrt(value);
        }
    }
}

class Cube : Shape
{
    public double side;

    public Cube(double s)
    {
        side = s;
    }

    public override double Area
    {
        get
        {
            return 6 * side * side;
        }
        set
        {
            side = System.Math.Sqrt(value / 6);
        }
    }
}

class TestShapes
{
    static void Main()
    {
        // Input the side:
        System.Console.Write("Enter the side: ");
        double side = double.Parse(System.Console.ReadLine());

        // Compute the areas:
        Square s = new Square(side);
        Cube c = new Cube(side);

        // Display the results:
        System.Console.WriteLine("Area of the square = {0:F2}", s.Area);
        System.Console.WriteLine("Area of the cube = {0:F2}", c.Area);
        System.Console.WriteLine();

        // Input the area:
        System.Console.Write("Enter the area: ");
        double area = double.Parse(System.Console.ReadLine());

        // Compute the sides:
        s.Area = area;
        c.Area = area;

        // Display the results:
        System.Console.WriteLine("Side of the square = {0:F2}", s.side);
        System.Console.WriteLine("Side of the cube = {0:F2}", c.side);
    }
}
/* Example Output:
    Enter the side: 4
    Area of the square = 16.00
    Area of the cube = 96.00

    Enter the area: 24
    Side of the square = 4.90
    Side of the cube = 2.00
*/

請參閱

參考

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

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

自動實作的屬性 (C# 程式設計手冊)

概念

C# 程式設計手冊