首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 深度解析:欄位與屬性的設計藝術——從 readonly 到屬性繼承

     編輯:關於C#

前言:封裝的核心——欄位與屬性

在 C# 物件導向設計中,「欄位」儲存資料,「屬性」控制存取。雖然編譯器將屬性降級為 get_PropertyName/set_PropertyName 兩個方法,但語言層面的語法糖讓封裝變得優雅且安全。本文深入探討欄位修飾符、readonly vs const 差異、屬性的三種存取模式、繼承語意與進階模式。

一、欄位:實例 vs 靜態、存取修飾符與唯讀語意

1.1 實例欄位 vs 靜態欄位

  • 實例欄位:屬於具體物件,每個實例擁有獨立副本。只能透過實例存取(obj.field)。
  • 靜態欄位:屬於類型本身,所有實例共享單一儲存位置。只能透過類型名稱存取(TypeName.field),不可透過實例存取(編譯錯誤)。

1.2 五種存取修飾符

修飾符可見範圍
public任意程式碼
protected本類別與衍生類別
internal同一組件內所有類別
protected internal同一組件內所有類別 + 衍生類別(跨組件亦可)
private僅本類別

實務建議:欄位預設 private,對外暴露請改用屬性,符合「封裝」原則。

1.3 readonly vs const:編譯期 vs 執行期常數

特性constreadonly
初始化時機宣告時(編譯期常數)宣告時或建構函式中(執行期常數)
儲存位置編譯期內嵌至呼叫端 IL(常數傳播)執行期讀取欄位值
型別限制僅限實值型別、string、enum、null 參考任意型別
靜態性隱含 static可為實例或靜態
版本控制影響修改值需重新編譯所有參考組件僅需重新編譯定義組件

經典案例:跨組件常數共享時,若未來可能調整數值,請使用 static readonly 避免版本衝突。

二、屬性:語法糖背後的方法契約

2.1 三種存取模式

// 讀寫屬性
public int Age { get; set; }

// 唯讀屬性(C# 6+ 可僅寫 get)
public string Name { get; }

// 唯寫屬性(極少見,通常用於 WriteOnly 集合)
public string Password { set; }

關鍵規則:同名屬性的 get/set 必須宣告在同一個屬性區塊內,不可分離(否則編譯錯誤 CS0111)。

2.2 屬性內的邏輯驗證

private string _name;
public string Name
{
    get => _name;
    set => _name = string.IsNullOrWhiteSpace(value) ? "預設名稱" : value.Trim();
}

屬性本質是方法,可執行任意邏輯:快取、延遲初始化、事件通知、權限檢查等。

2.3 繼承與多型:virtual、override、sealed、abstract

public abstract class Animal
{
    public virtual string Sound { get; } = "...";
    public abstract string Name { get; set; }
}

public class Dog : Animal
{
    public sealed override string Sound => "汪汪"; // 封鎖進一步覆寫
    public override string Name { get; set; }
}
  • virtual:允許衍生類別 override(執行期繫結)。
  • override:覆寫基類虛擬/抽象屬性。
  • sealed override:覆寫並禁止後續衍生類別再次覆寫。
  • abstract:強制衍生類別實作(類別亦須標記 abstract)。
  • new:隱藏基類同名屬性(編譯期繫結,非多型)。

2.4 靜態屬性與外部屬性

  • 靜態屬性(static)僅能存取靜態欄位,呼叫方式 TypeName.Prop。
  • 外部屬性(extern)搭配 DllImport 對應非受控程式碼的 getter/setter,極少見但合法。

三、進階模式:自動實作屬性、init 存取子、required 修飾符

3.1 自動實作屬性(C# 3.0+)

public int Id { get; set; } // 編譯器自動產生隱私支援欄位

3.2 init 存取子(C# 9.0+)—— 物件初始化後不可變

public class Person
{
    public string Name { get; init; } // 僅能在物件初始化器中設定
}
var p = new Person { Name = "Tom" };
// p.Name = "Jerry"; // 編譯錯誤

3.3 required 修飾符(C# 11+)—— 強制初始化

public class Config
{
    public required string ConnectionString { get; init; }
}
var cfg = new Config { ConnectionString = "..." }; // 必填,否則編譯警告/錯誤

四、效能考量與最佳實務

  1. 避免在屬性 getter 執行高成本運算(如資料庫查詢、複雜計算),改用方法(GetSomething())明確語意。
  2. 大型結構體(>16 bytes)建議用 readonly struct + 唯讀屬性,減少防禦性複製開銷。
  3. 熱路徑屬性可標記 [MethodImpl(MethodImplOptions.AggressiveInlining)] 內聯優化。
  4. 序列化:System.Text.Json 預設支援公用屬性;DataContractSerializer 需 [DataMember] 標記。

結語

欄位是資料的容器,屬性是資料的守門人。善用 readonly/const 表達不變性,透過屬性封裝驗證邏輯,配合 virtual/override/sealed 設計可擴充的繼承體系,並善用現代 C#(init、required、record)打造不可變資料模型,即能寫出健壯、可維護且效能優異的 C# 程式碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved