指派運算子是程式狀態變更的核心機制。C# 提供簡單指派 (=) 與 10 種複合指派 (+=, -= 等),並具備右結合性與嚴格的類型轉換規則。掌握其語意細節,能避免隱性 Bug 與效能陷阱。
| 分類 | 運算子 | 說明 |
|---|---|---|
| 簡單指派 | = | 基本指派 |
| 複合指派 | ||
| 算術 | += | 加法指派 |
-= | 減法指派 | |
*= | 乘法指派 | |
/= | 除法指派 | |
%= | 餘數指派 | |
| 位元 | ||
&= | 位元 AND 指派 | |
|= | 位元 OR 指派 | |
^= | 位元 XOR 指派 | |
| 移位 | ||
<<= | 左移指派 | |
>>= | 右移指派 | |
左運算元必須為:
readonly,建構函式除外)set 存取子)set 存取子)// 合法 int x = 0; x = 1; obj.Field = 1; obj.Property = 1; arr[0] = 1; // 非法 5 = 1; // 常數 readonlyField = 1; // 唯讀欄位 (建構函式外) obj.ReadOnlyProp = 1; // 唯讀屬性
// 右結合:a = b = c 等價於 a = (b = c) // 執行順序:先算 c,指派給 b,結果再指派給 a int a, b, c; a = b = c = 10; // 依序:c=10, b=10, a=10 // 實戰:鏈式初始化 var x = y = z = DefaultValue(); // 單次呼叫,三變數同值
// 右運算元型別必須隱含轉換為左運算元型別 int i = 10; long l = i; // ✅ int → long (隱含) short s = i; // ❌ int → short (需顯式轉型) float f = l; // ✅ long → float (隱含,可能精度遺失)
int x; Console.WriteLine(x = 5); // 輸出 5,x = 5 // 巢狀指派 int y = (x = 10) + 5; // x=10, 運算式值=10, y=15
x op= y 等價於 x = x op y,但 x 只評估一次 (對屬性/索引子至關重要)。
| 規則 | 條件 | 轉換 | 範例 |
|---|---|---|---|
| 1. 隱含轉換 | x op y 結果隱含轉換為 x 型別 | x = x op y | int x=1; x += 2; |
| 2. 顯式轉換 (預定義運算子) | 結果可顯式轉換為 x,y 可隱含轉換為 x | x = (T)(x op y) | byte b=1; b += 2; (int→byte) |
| 3. 無效 | 以上皆不滿足 | 編譯錯誤 | string s="a"; s += 1; |
// 屬性/索引子只存取一次 obj.Prop += 1; // 等價於 (非真實碼) var temp = obj.Prop; obj.Prop = temp + 1; // 只呼叫 1 次 get, 1 次 set // 陣列索引 arr[GetIndex()] += 1; // GetIndex() 只呼叫一次!
[Flags] enum Perm { Read = 1, Write = 2, Exec = 4 }
Perm p = Perm.Read;
p |= Perm.Write; // p = Read | Write
p &= ~Perm.Write; // 移除 Write
p ^= Perm.Exec; // 切換 Exec
event Action OnEvent; OnEvent += Handler; // 訂閱 OnEvent -= Handler; // 取消訂閱
x += y + z; // x = x + (y + z) — + 優先級高於 += x *= y + z; // x = x * (y + z) // 建議:明確括號 x += (y + z);
struct Point { public int X; }
var p = new Point();
p.X += 1; // ⚠️ 若 Point 為 class 則正常,struct 可能編譯錯誤 (取決於語境)
// 正確:整體替換
p = new Point { X = p.X + 1 };
byte b = 10; int i = b; // ✅ byte → int long l = i; // ✅ int → long float f = l; // ✅ long → float (精度可能遺失)
int i = 300; byte b = (byte)i; // 44 (截斷) long l = 100; int j = (int)l; // ✅ 明確轉型
struct Celsius { public double Value;
public static implicit operator double(Celsius c) => c.Value;
public static explicit operator Celsius(double d) => new Celsius { Value = d }; }
Celsius c = 25.0; // 隱含 double → Celsius
double d = c; // 隱含 Celsius → double
Celsius c2 = (Celsius)100.0; // 顯式 double → Celsius
x += 1 優於 x = x + 1 (單次評估、意圖明確)a = b = c = Compute() (副作用順序難追蹤)flags |= Flag 標準慣用語精通指派運算子的右結合性、單次評估保證與三大複合指派規則,能編寫更安全、高效且符合 C# 慣用語的程式碼。