Tip
本文屬於 基礎部分, 適合已經至少懂一種程式語言並正在學習 C# 的開發者。 如果你是程式新手,建議先從 入門 教學開始。
來自另一種語言? C# 中的表達式運作方式與 Java、C++ 和 JavaScript 相似。 值得注意的一個差異是:複合指派運算子如 += 和 increment 運算子 ++ 是 C# 中的表達式,因此可以出現在較大的表達式中。
表達式是編譯器評估以產生值的一段程式碼——數字、字串、參考bool或 。 例如, 3 + 4 * 2 是一個將 值為整數 11的表達式,而 total > 10 是一個將 值為 true 或 false的表達式。
最簡單的表達式是 文字 (如 42 或 "hello")和 變數名稱 (如 total)。 你可以透過將較簡單的表達式與運算子結合來構建更複雜的表達式。
表達式和語句
一個表達式產生一個值。
語句是程式執行的完整指令。 許多語句包含表達式。 例如, int total = 3 + 4 * 2; 是一個變數宣告語句。 編譯器會評估初始化運算式 3 + 4 * 2,產生 11,並將該值指派給新變數 total。
你可以把一個表達式想像成片語,一個 C# 語句 就像一個完整的句子。 這些工作定義幫助你理解在學習 C# 時會遇到的正式術語和說明,雖然一般程式設計很少需要你有意識地記住這些區別。
組合表達式
表達式可以組合使用。 請考慮 3 + 4 * 2。 這個單一表達式實際上包含兩個較小的表達式: 乘法表達4 * 2 式和 加法表達3 + <result>式。 當表達式組合時,C# 需要一條規則來決定先評估哪一個。 那條規則就是 運算子優先順序。
你不需要背誦完整的優先順序。 下一節總結日常群組,括號中通常能讓你在不確定時明確排序。
運算元優先順序
運算子優先順序決定組合運算式如何分組為子運算式。 這個概念類似數學課上的運算順序規則。 優先順序較高的運算子會與其運算元結合得更緊密。 表達式的結構就像是這些運算元被括號括在一起。 在 3 + 4 * 2中 * ,優先順序高於 +,因此表達式群為 3 + (4 * 2),而非 (3 + 4) * 2。
C# 的優先順序群組比這裡總結的四個還多。 這些其他群組遵循大家熟悉的規則——例如乘法先於加法——並涵蓋完整的運算子集。 以下群組涵蓋你在日常程式碼中最常遇到的類型:
-
初級、一元和範圍 ——這三個不同的優先順序群,彼此的連結比算術更緊密。 這個摘要將它們合併為一個步驟,因為實際上它們都適用於算術之前。
-
主要運算子—成員存取(
x.y)、方法呼叫(f())、索引(a[i])及 null 條件存取(?.、?[])—在所有運算子中結合得最緊密。 -
一元運算子作用於單一運算元:否定(
-x)、邏輯 NOT(!flag)、前綴增量(++i),以及後綴增量(i++)。 -
範圍運算子(
..)用於建立切片表達式的索引範圍,例如array[1..4]。
-
主要運算子—成員存取(
-
算術 —
*、/、%比+和-結合得更緊密。 乘法和除法發生在加法和減法之前。 -
比較 —
<、>、<=>=、==!=的束縛比算術綁定得較寬鬆,因此算術在比較前完成。 -
邏輯 —
&&和||在常見運算子中束縛最不緊密,因此會先完成比較,再進行邏輯組合。
這個優先順序表示,表達式 score + bonus > threshold && attempts < maxAttempts 的求值方式和你讀到的順序完全一致:將 score 和 bonus 相加,將總和與 threshold 比較,將 maxAttempts 與 attempts 比較,然後再以 && 結合兩個 bool 的結果。 加上括號使每個分組明確化,結構相同: ((score + bonus) > threshold) && (attempts < maxAttempts)。
關於涵蓋所有運算子的完整優先順序階層,請參見 C# 運算子與表達式。
用括號來明確表達意圖
括號可以優先處理優先順序,同時記錄你的意圖。 當上述群組的順序不明顯時,請加上括號:
int score = 80;
int bonus = 15;
int threshold = 90;
bool eligible = true;
// Without parentheses: || binds less tightly than &&, so this reads as:
// eligible && (score + bonus > threshold) || bonus > 20
bool result1 = eligible && score + bonus > threshold || bonus > 20;
// With parentheses: forces the || to combine two complete conditions
bool result2 = (eligible && score + bonus > threshold) || bonus > 20;
Console.WriteLine(result1); // => True
Console.WriteLine(result2); // => True
// Parentheses can also change the result:
bool isAdmin = false;
bool isOwner = true;
// Without: && binds tighter, so: (isAdmin && isOwner) || true
bool access1 = isAdmin && isOwner || true;
// With: forces the || to run first
bool access2 = isAdmin && (isOwner || true);
Console.WriteLine(access1); // => True (false && true is false; false || true is true)
Console.WriteLine(access2); // => False (false && anything is false)
最後兩行顯示,括號可以改變結果,而不只是樣式。 當 && 和 || 同時出現時,括號內標示哪個條件先結合。 看到 (isAdmin && isOwner) || isSuperUser 的讀者會立刻明白其意圖。
表達式的評估方式
理解 C# 如何評估組合式,使用類比會更容易。 想像用鉛筆和紙在解一個複雜的數學題:你找出最內層或最高優先順序的子表達式,計算其中間值,寫下結果,然後重複下一個子表達式——持續直到得到最終答案。
C# 遵循相同的流程,並遵循兩條規則:
規則一:運算元從左到右評估。 對於任何二元運算式,兩個運算元都必須在套用運算子之前完成求值。 C# 先評估左操作元,再評估右操作元,然後執行操作。
規則二:有些操作員會短路。 某些運算子在結果確定後會立即停止計算,跳過剩餘的運算元:
-
&&(條件 AND):當左側為false時,會立即返回false。 右側從不被評估。 -
||(條件 OR):當左側變true時返回true。 右側從不被評估。 -
?:(條件/三元):只評估符合條件的分支——另一個分支永遠不會被評估。 -
?.(空條件成員存取)和?[](空條件元素存取):當左側為null時立即停止並返回null,跳過成員存取或索引。 -
??=(空合併指派):僅當左側為null時才指派右側。
紙筆評估
考慮運算式 3 + 6 / 2。 雖然依照讀取順序,加法最先出現,但 / 的優先順序高於 +,因此 6 / 2 是最先被求值的子運算式。 一步步來——就像你在紙上做的一樣:
3 + 6 / 2
↓ (evaluate 6 / 2 → 3)
3 + 3
↓ (evaluate 3 + 3 → 6)
6
// 3 + 6 / 2
// Step 1: 6 / 2 has higher precedence → interim value 3
// Step 2: 3 + 3 → final result 6
int result = 3 + 6 / 2;
Console.WriteLine(result); // => 6
由6 / 2產生的中間值3成為 的+右運算元,最終結果為 6。 每個子表達式產生一個中間值;這些中間值會餵入下一個子運算式,直到只剩下一個值。
結合性 是一個相關概念:當兩個算子具有相同的優先順序時,結合性決定哪一個先發生。 大多數 C# 運算子都是 左結合,也就是從左到右分組。 因此 a - b - c 與 (a - b) - c相同,不是 a - (b - c)。
短路評估的實務
短路求值對於 null 檢查特別有用:
string? text = null;
// Safe: second condition runs only when text is not null
bool hasContent = text != null && text.Length > 0;
Console.WriteLine(hasContent); // => False (short-circuits after null check; no NullReferenceException)
text = "hello";
hasContent = text != null && text.Length > 0;
Console.WriteLine(hasContent); // => True
// || short-circuits on true: right side is never evaluated when left side is true
string word = "hello";
bool anyMatch = word.StartsWith("h") || word.StartsWith("x");
Console.WriteLine(anyMatch); // => True (right side never evaluated)
// ?. short-circuits on null: returns null without accessing .Length
string? maybeNull = null;
int? length = maybeNull?.Length; // length is null; no NullReferenceException
Console.WriteLine(length.HasValue); // => False
text != null && text.Length > 0 是安全的,因為第二個條件只有在 text 不 null時才會執行。 同理,?. 在遇到 null 參照時會停止評估,從而避免在沒有明確 NullReferenceException 檢查的情況下發生 if。
欲更廣泛了解空安全運算子,請參見 C# 空運算子。
另請參閱
- C# 運算子與表達式(語言參考) — 完整優先順序表及每個運算元
- 算術、比較、邏輯與賦值運算子 ——日常運算子的深度分析
-
相等性比較——
==、!=和Equals如何運作 -
C# 空運算元 —
?.、??、??= - 布林值邏輯運算子