加法運算 (+) 是 C# 最多型的運算子,支援數值加法、列舉位移、字串串接、委派合併與 TimeSpan 運算。各類型語意截然不同,且涉及溢位、精度、不可變性等關鍵特性。深入掌握這些細節,是正確建模領域邏輯與避免效能陷阱的基礎。
支援加法的類型總覽
| 類型分類 | 形式 | 語意 | 結果類型 | 關鍵特性 |
| 整數/浮點數/decimal | a + b | 數值加法 | 較大類型 | 溢位/精度/IEEE 754 |
| 列舉 | E + U, U + E | 基底值位移 | E | 不支援 E + E |
| 字串 | s + s, s + obj | 串接 | string | 不可變、配置新物件 |
| 委派 | D + D | 合併調用清單 | D | 不可變、null 安全 |
| TimeSpan | TS + TS, DT + TS | 時間累加 | TimeSpan / DateTime | 溢位例外 |
| 指標 (unsafe) | T* + n | 指標位移 | T* | 元素大小倍數 |
數值加法:溢位與 IEEE 754 特殊值
整數溢位
int max = int.MaxValue; // 2147483647
// unchecked (預設):環繞
var wrap = max + 1; // -2147483648 (MinValue)
// checked:拋出 OverflowException
checked { max + 1; } // Exception
// 長整數避免溢位
long safe = (long)max + 1; // 2147483648L
浮點數特殊值表
| a + b | 結果 | 說明 |
| ∞ + ∞ | ∞ | 同號無窮大相加 |
| ∞ + (-∞) | NaN | 不定式 |
| 正常 + ∞ | ∞ | 吸收 |
| +0.0 + -0.0 | +0.0 | 正零優先 |
| NaN + 任何 | NaN | 傳播 |
decimal 加法
decimal a = 0.1m, b = 0.2m;
decimal c = a + b; // 0.3m (精確,無二進位誤差)
// 範圍:±7.9e28,溢位拋出 OverflowException
decimal.MaxValue + 1; // OverflowException
列舉加法:基底值位移 (非旗標合併)
enum Weekday { Sun=0, Mon=1, Tue=2, Wed=3, Thu=4, Fri=5, Sat=6 }
// E + U → E (位移)
Weekday d = Weekday.Mon + 3; // Weekday.Thu (值 4)
// U + E → E (相同)
Weekday d2 = 3 + Weekday.Mon; // Weekday.Thu
// 底層機制:(E)((U)E + U) — 基底整數加法後轉回列舉
// 注意:結果可能無對應列舉成員 (如 Mon + 10 = 11)
// 旗標合併請用位元 OR:FlagsEnum.A | FlagsEnum.B
實戰範例:日期計算
enum Month { Jan=1, Feb=2, ..., Dec=12 }
Month m = Month.Jan;
m = m + 3; // Month.Apr (值 4)
// 循環位移需自行處理
Month NextMonth(Month m, int offset)
=> (Month)((((int)m - 1 + offset) % 12) + 1);
字串串接:不可變性與效能
多載形式
string + string → string
string + object → string (呼叫 ToString())
object + string → string (呼叫 ToString())
// 注意:object + object 無 + 多載 (編譯錯誤)
編譯器優化:單一陳述式內常數摺疊
// 編譯時期合併
string s = "Hello" + " " + "World"; // "Hello World" (單一字面常數)
// 執行期:每次 + 都配置新字串
string a = "A";
string b = "B";
string c = a + b; // 新配置 "AB"
string d = c + "C"; // 新配置 "ABC"
// 迴圈中災難性效能
string result = "";
for (int i = 0; i < 1000; i++)
result += i.ToString(); // 1000 次配置,O(n²)
// 正確:StringBuilder
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
sb.Append(i);
string efficient = sb.ToString(); // 單次配置
// C# 10+:字串插值優化
string modern = $"{a}{b}{c}"; // 編譯為 StringBuilder 或 String.Concat
String.Concat vs StringBuilder
// 少量 (≤4) 字串:Concat 最佳
string s = String.Concat(a, b, c, d);
// 大量/動態:StringBuilder
var sb = new StringBuilder(capacity: 1000); // 預分配避免擴容
委派合併:不可變多播
語意規則
Action a1 = () => Console.WriteLine("A");
Action a2 = () => Console.WriteLine("B");
Action multi = a1 + a2; // 新委派,清單 [a1, a2]
// a1, a2 不變
// null 安全
Action nullAct = null;
Action combo = a1 + nullAct; // [a1]
combo = nullAct + a1; // [a1]
// 相同委派可重複合併
Action dup = a1 + a1; // [a1, a1] — 執行兩次 A
事件訂閱語法糖
event Action OnEvent;
OnEvent += Handler; // 實為 OnEvent = OnEvent + Handler
OnEvent -= Handler; // 實為 OnEvent = OnEvent - Handler
TimeSpan/DateTime 加法
TimeSpan t1 = TimeSpan.FromHours(2);
TimeSpan t2 = TimeSpan.FromMinutes(30);
TimeSpan sum = t1 + t2; // 2.5 小時
DateTime now = DateTime.Now;
DateTime future = now + TimeSpan.FromDays(7); // 一週後
// DateTime + DateTime 不支援 (無語意)
// 溢位檢查
DateTime.MaxValue + TimeSpan.FromSeconds(1); // OverflowException
使用者定義型別多載標準模式
public readonly struct Vector3
{
public double X, Y, Z;
public static Vector3 operator +(Vector3 a, Vector3 b)
=> new Vector3 { X = a.X + b.X, Y = a.Y + b.Y, Z = a.Z + b.Z };
// 標量加法 (不常見,通常用乘法)
public static Vector3 operator +(Vector3 v, double k)
=> new Vector3 { X = v.X + k, Y = v.Y + k, Z = v.Z + k };
// 交換律支援
public static Vector3 operator +(double k, Vector3 v) => v + k;
}
效能特性對比
| 類型 | 單次操作成本 | 記憶體配置 | 最佳實踐 |
| 整數/浮點數 | 1-5 週期 | 無 (暫存器) | 直接使用 |
| decimal | ~50-100 週期 | 無 | 避免熱路徑 |
| 字串 | O(n) 配置+複製 | 每次新字串 | StringBuilder / 插值 |
| 委派 | ~微秒 | 新陣列+委派物件 | 快取合併結果 |
| TimeSpan | ~10 週期 | 無 (結構) | 直接使用 |
最佳實踐總結
- 整數加法溢位風險 — 用
checked、long 或 TryAdd (Core 2.0+)
- 浮點數 ∞ + (-∞) = NaN — 檢查特殊值避免污染
- 字串串接迴路必用 StringBuilder — 或 C# 10+ 插值語法
- 列舉加法僅支援 E+U — 旗標合併用
|,非 +
- 委派合併不可變 — 原委派不變,結果快取避免重複合併
- TimeSpan/DateTime 型別安全 — 編譯器防止無語意組合
- 自訂型別多載 + 遵循交換律/結合律 — 符合數學直覺
精通加法運算在各類型上的語意邊界與效能特性,能編寫正確、高效且符合領域直覺的程式碼。