From cba9c0ad081ee8addaf95d6e7e79523001350b8a Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 10 Aug 2026 15:40:22 -0400 Subject: [PATCH 01/11] Add expressions overview fundamentals article (#55333) New article: docs/csharp/fundamentals/expressions/index.md - Beginner-friendly overview: what expressions are, value-producing vs void - Operator precedence taught as three memorable tiers (arithmetic, comparison, logical) - No full precedence table; links to language-reference/operators/index.md - Parentheses section with executable snippet - Short-circuit evaluation section with executable snippet - TOC entry added under 'Expressions and statements' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2091fd4c-9f7b-4948-9282-a93f05502675 --- docs/csharp/fundamentals/expressions/index.md | 81 +++++++++++++++++++ .../snippets/expressions/Program.cs | 31 +++++++ .../snippets/expressions/expressions.csproj | 10 +++ docs/csharp/toc.yml | 2 + 4 files changed, 124 insertions(+) create mode 100644 docs/csharp/fundamentals/expressions/index.md create mode 100644 docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs create mode 100644 docs/csharp/fundamentals/expressions/snippets/expressions/expressions.csproj diff --git a/docs/csharp/fundamentals/expressions/index.md b/docs/csharp/fundamentals/expressions/index.md new file mode 100644 index 0000000000000..b47583cc9ab45 --- /dev/null +++ b/docs/csharp/fundamentals/expressions/index.md @@ -0,0 +1,81 @@ +--- +title: "C# expressions overview" +description: Learn how C# expressions work, how operator precedence determines evaluation order, how to use parentheses to make intent clear, and how short-circuit evaluation works. +ms.date: 08/10/2026 +ms.topic: concept-article +ai-usage: ai-assisted +--- + +# C# expressions + +> [!TIP] +> This article is part of the **Fundamentals** section for developers who already know at least one programming language and are learning C#. If you're new to programming, start with the [Get started](../../tour-of-csharp/tutorials/index.md) tutorials first. +> +> **Coming from another language?** Expressions in C# work much as they do in Java, C++, and JavaScript. One difference worth noting: compound assignment operators like `+=` and the increment operator `++` are expressions in C#, so they can appear in larger expressions. Prefer standalone statements for clarity. + +An *expression* is a piece of code that the compiler evaluates. Most expressions produce a value — a number, a string, a reference, a `bool` — that you can assign, pass, or use in a larger expression. Some expressions, such as a call to a `void` method, run for their side effects and produce no value. + +```csharp +int total = 3 + 4 * 2; // arithmetic expression → value 11 +bool isReady = total > 10; // comparison expression → value false +Console.WriteLine("done"); // void method call → no value +``` + +The simplest expressions are *literals* (like `42` or `"hello"`) and *variable names*. You build more complex expressions by combining these with operators. + +## Operator precedence + +When an expression contains multiple operators, C# follows *operator precedence* rules to decide which operation to evaluate first — the same idea as the order-of-operations rules you learned in math class. + +Memorizing three tiers covers most real code: + +1. **Arithmetic first** — `*`, `/`, `%` bind tighter than `+` and `-`. Multiplication and division happen before addition and subtraction. +2. **Comparison next** — `<`, `>`, `<=`, `>=`, `==`, `!=` bind less tightly than arithmetic, so arithmetic completes before the comparison. +3. **Logical last** — `&&` and `||` bind least tightly of the common operators, so comparisons complete before the logical combination. + +This means the expression `score + bonus > threshold && attempts < maxAttempts` evaluates exactly as you'd read it in English: add score and bonus, compare to threshold, compare attempts to maxAttempts, then combine the two `bool` results. + +The full operator precedence table — covering every operator — lives in the language reference: [C# operators and expressions](../../language-reference/operators/index.md). + +### Use parentheses to make intent clear + +Parentheses override precedence and document your intent at the same time. When the order isn't obvious from the three tiers above, add parentheses: + +:::code language="csharp" source="snippets/expressions/Program.cs" ID="ParenthesesClarity"::: + +Prefer parentheses over memorizing obscure precedence rules. A reader who sees `(a | b) & c` knows exactly what you intended; `a | b & c` requires them to recall that `&` binds more tightly than `|`. + +## Operand evaluation order + +Regardless of precedence, C# evaluates the *operands* of most operators from left to right before applying the operator. Precedence determines which operator applies to which operands; left-to-right evaluation determines when each operand expression runs. + +```csharp +int x = 1; +int result = (x++) + (x++); // first operand: x=1→2; second operand: x=2→3; result = 1 + 2 = 3 +``` + +This distinction rarely matters unless operands have side effects. In practice, write each side effect on its own statement for clarity. + +## Associativity + +When two operators have the same precedence level, *associativity* decides which one applies first. Most C# operators are *left-associative*: `a - b - c` groups as `(a - b) - c`. Assignment operators are *right-associative*: `a = b = 0` groups as `a = (b = 0)`, so `b` is set first, then `a` is set to the same value. + +## Short-circuit evaluation + +The logical operators `&&` (AND) and `||` (OR) are *short-circuit* operators: they stop evaluating as soon as the result is known. + +- `&&` returns `false` as soon as the left side is `false`. The right side is never evaluated. +- `||` returns `true` as soon as the left side is `true`. The right side is never evaluated. + +:::code language="csharp" source="snippets/expressions/Program.cs" ID="ShortCircuit"::: + +Short-circuit evaluation is useful for null checks: `text != null && text.Length > 0` is safe because the second condition runs only when `text` is not null. For a broader look at null-safe operators, see [C# null operators](../null-safety/null-operators.md). + +The non-short-circuit alternatives `&` and `|` always evaluate both sides. Use them only when the right-side side effect must always run. + +## See also + +- [C# operators and expressions (language reference)](../../language-reference/operators/index.md) — full precedence table and every operator +- [Equality comparisons](equality.md) — how `==`, `!=`, and `Equals` work +- [C# null operators](../null-safety/null-operators.md) — `?.`, `??`, and `??=` +- [Boolean logical operators](../../language-reference/operators/boolean-logical-operators.md) diff --git a/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs b/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs new file mode 100644 index 0000000000000..f6d9ae3cdc0da --- /dev/null +++ b/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs @@ -0,0 +1,31 @@ +// +int a = 3; +int b = 4; +int c = 2; + +// Without parentheses: relies on knowing that & binds tighter than | +bool result1 = a > 1 | b > 1 & c > 1; // evaluated as: a > 1 | (b > 1 & c > 1) + +// With parentheses: intent is explicit +bool result2 = (a > 1 | b > 1) & c > 1; // parentheses force | before & + +Console.WriteLine(result1); // => True +Console.WriteLine(result2); // => True (same result here, but intent is unambiguous) +// + +// +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) +// diff --git a/docs/csharp/fundamentals/expressions/snippets/expressions/expressions.csproj b/docs/csharp/fundamentals/expressions/snippets/expressions/expressions.csproj new file mode 100644 index 0000000000000..dfb40caafcf9a --- /dev/null +++ b/docs/csharp/fundamentals/expressions/snippets/expressions/expressions.csproj @@ -0,0 +1,10 @@ + + + + Exe + net10.0 + enable + enable + + + diff --git a/docs/csharp/toc.yml b/docs/csharp/toc.yml index 5284db7e654c5..3e5ee8035ff78 100644 --- a/docs/csharp/toc.yml +++ b/docs/csharp/toc.yml @@ -115,6 +115,8 @@ items: href: fundamentals/tutorials/string-interpolation.md - name: Expressions and statements items: + - name: Expressions overview + href: fundamentals/expressions/index.md - name: Equality href: fundamentals/expressions/equality.md - name: Selection statements From 3a227e0d76af07bdcb9b994e74632645d86e53d2 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 10 Aug 2026 15:45:02 -0400 Subject: [PATCH 02/11] Refine expressions overview: beginner-friendly examples and remove advanced details - Replace boolean &/| ParenthesesClarity snippet with &&/|| example that clearly shows parentheses changing the result (isAdmin/isOwner pattern) - Remove non-short-circuit &/| sentence from Short-circuit section - Trim Associativity to left-associative only; drop chained assignment - Replace (x++)+(x++) operand-order example with a clean arithmetic example Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2091fd4c-9f7b-4948-9282-a93f05502675 --- docs/csharp/fundamentals/expressions/index.md | 18 ++++++----- .../snippets/expressions/Program.cs | 30 ++++++++++++++----- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/docs/csharp/fundamentals/expressions/index.md b/docs/csharp/fundamentals/expressions/index.md index b47583cc9ab45..105d9bb8d3e29 100644 --- a/docs/csharp/fundamentals/expressions/index.md +++ b/docs/csharp/fundamentals/expressions/index.md @@ -43,22 +43,26 @@ Parentheses override precedence and document your intent at the same time. When :::code language="csharp" source="snippets/expressions/Program.cs" ID="ParenthesesClarity"::: -Prefer parentheses over memorizing obscure precedence rules. A reader who sees `(a | b) & c` knows exactly what you intended; `a | b & c` requires them to recall that `&` binds more tightly than `|`. +The last two lines show that parentheses can change the result, not just the style. When `&&` and `||` appear together, add parentheses to spell out which condition combines first. A reader who sees `(isAdmin && isOwner) || isSuperUser` knows the intent immediately. ## Operand evaluation order -Regardless of precedence, C# evaluates the *operands* of most operators from left to right before applying the operator. Precedence determines which operator applies to which operands; left-to-right evaluation determines when each operand expression runs. +Regardless of precedence, C# evaluates the *operands* of most operators from left to right before applying the operator. Precedence determines which operator applies to which operands; left-to-right evaluation determines the order in which sub-expressions run. ```csharp -int x = 1; -int result = (x++) + (x++); // first operand: x=1→2; second operand: x=2→3; result = 1 + 2 = 3 +int a = 6; +int b = 2; +int c = 3; + +// a / b is evaluated before + c, because / has higher precedence than + +int result = a / b + c; // (6 / 2) + 3 = 6 ``` -This distinction rarely matters unless operands have side effects. In practice, write each side effect on its own statement for clarity. +This distinction rarely matters in everyday code. In practice, use parentheses or separate statements when you need a specific order. ## Associativity -When two operators have the same precedence level, *associativity* decides which one applies first. Most C# operators are *left-associative*: `a - b - c` groups as `(a - b) - c`. Assignment operators are *right-associative*: `a = b = 0` groups as `a = (b = 0)`, so `b` is set first, then `a` is set to the same value. +When two operators have the same precedence level, *associativity* decides which one applies first. Most C# operators are *left-associative*: `a - b - c` groups as `(a - b) - c`, working left to right. ## Short-circuit evaluation @@ -71,8 +75,6 @@ The logical operators `&&` (AND) and `||` (OR) are *short-circuit* operators: th Short-circuit evaluation is useful for null checks: `text != null && text.Length > 0` is safe because the second condition runs only when `text` is not null. For a broader look at null-safe operators, see [C# null operators](../null-safety/null-operators.md). -The non-short-circuit alternatives `&` and `|` always evaluate both sides. Use them only when the right-side side effect must always run. - ## See also - [C# operators and expressions (language reference)](../../language-reference/operators/index.md) — full precedence table and every operator diff --git a/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs b/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs index f6d9ae3cdc0da..2cef403d94b71 100644 --- a/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs +++ b/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs @@ -1,16 +1,30 @@ // -int a = 3; -int b = 4; -int c = 2; +int score = 80; +int bonus = 15; +int threshold = 90; +bool eligible = true; -// Without parentheses: relies on knowing that & binds tighter than | -bool result1 = a > 1 | b > 1 & c > 1; // evaluated as: a > 1 | (b > 1 & c > 1) +// 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: intent is explicit -bool result2 = (a > 1 | b > 1) & c > 1; // parentheses force | before & +// 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 (same result here, but intent is unambiguous) +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 (true || anything is true) +Console.WriteLine(access2); // => False (false && anything is false) // // From 6a9f370547d430af23b4d39732d3844083d537d0 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 12 Aug 2026 17:06:42 -0400 Subject: [PATCH 03/11] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/csharp/fundamentals/expressions/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/fundamentals/expressions/index.md b/docs/csharp/fundamentals/expressions/index.md index 105d9bb8d3e29..623f35f1ff698 100644 --- a/docs/csharp/fundamentals/expressions/index.md +++ b/docs/csharp/fundamentals/expressions/index.md @@ -17,7 +17,7 @@ An *expression* is a piece of code that the compiler evaluates. Most expressions ```csharp int total = 3 + 4 * 2; // arithmetic expression → value 11 -bool isReady = total > 10; // comparison expression → value false +bool isReady = total > 10; // comparison expression → value true Console.WriteLine("done"); // void method call → no value ``` From 5fdb40b33c0862e7e4a1f6200974a51397e82a92 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 13 Aug 2026 15:05:41 -0400 Subject: [PATCH 04/11] Rewrite expressions overview as beginner learning material - Remove odd tip sentence 'Prefer standalone statements for clarity' - Intro now uses inline expressions (3 + 4 * 2, total > 10) instead of compilable statement snippets; adds expression-vs-statement comparison - New 'Combining expressions' section explains why precedence matters and notes readers can use parentheses instead of memorizing rules - Rewrites 'Operand evaluation order' around two clear rules: left-to-right evaluation and short-circuit operators; uses paper-and-pencil analogy - Adds StepByStep snippet showing interim-value computation - Expands short-circuit coverage to include ?:, ?., ?[], and ??= - Folds associativity into evaluation section as a small definition rather than a standalone heading - Fixes all review typos (comparision, short circut, Mayble) in polished prose Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9b155fe4-7996-4e57-ae91-d560587cc26d --- docs/csharp/fundamentals/expressions/index.md | 70 +++++++++++-------- .../snippets/expressions/Program.cs | 16 +++++ 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/docs/csharp/fundamentals/expressions/index.md b/docs/csharp/fundamentals/expressions/index.md index 623f35f1ff698..acad83a485330 100644 --- a/docs/csharp/fundamentals/expressions/index.md +++ b/docs/csharp/fundamentals/expressions/index.md @@ -1,7 +1,7 @@ --- title: "C# expressions overview" -description: Learn how C# expressions work, how operator precedence determines evaluation order, how to use parentheses to make intent clear, and how short-circuit evaluation works. -ms.date: 08/10/2026 +description: Learn how C# expressions work, how operator precedence and evaluation order determine results, how to use parentheses for clarity, and how short-circuit evaluation works. +ms.date: 08/13/2026 ms.topic: concept-article ai-usage: ai-assisted --- @@ -11,29 +11,33 @@ ai-usage: ai-assisted > [!TIP] > This article is part of the **Fundamentals** section for developers who already know at least one programming language and are learning C#. If you're new to programming, start with the [Get started](../../tour-of-csharp/tutorials/index.md) tutorials first. > -> **Coming from another language?** Expressions in C# work much as they do in Java, C++, and JavaScript. One difference worth noting: compound assignment operators like `+=` and the increment operator `++` are expressions in C#, so they can appear in larger expressions. Prefer standalone statements for clarity. +> **Coming from another language?** Expressions in C# work much as they do in Java, C++, and JavaScript. One difference worth noting: compound assignment operators like `+=` and the increment operator `++` are expressions in C#, so they can appear in larger expressions. -An *expression* is a piece of code that the compiler evaluates. Most expressions produce a value — a number, a string, a reference, a `bool` — that you can assign, pass, or use in a larger expression. Some expressions, such as a call to a `void` method, run for their side effects and produce no value. +An *expression* is a piece of code that the compiler evaluates to produce a value — a number, a string, a reference, or a `bool`. For example, `3 + 4 * 2` is an expression that evaluates to the integer `11`, and `total > 10` is an expression that evaluates to `true` or `false`. -```csharp -int total = 3 + 4 * 2; // arithmetic expression → value 11 -bool isReady = total > 10; // comparison expression → value true -Console.WriteLine("done"); // void method call → no value -``` +The simplest expressions are *literals* (like `42` or `"hello"`) and *variable names* (like `total`). You build more complex expressions by combining simpler ones with operators. -The simplest expressions are *literals* (like `42` or `"hello"`) and *variable names*. You build more complex expressions by combining these with operators. +### Expressions and statements + +An expression produces a value. A *statement* is a complete instruction that the program executes — and many statements contain expressions. For example, in the statement `int total = 3 + 4 * 2;`, the expression `3 + 4 * 2` is evaluated first, and then the assignment statement stores the resulting value in `total`. This article focuses on expressions — how they're formed, how they're evaluated, and how they combine. + +## Combining expressions + +Expressions can be combined. Consider `3 + 4 * 2`. This single expression actually contains two smaller expressions: the *multiplication expression* `4 * 2` and the *addition expression* `3 + `. When expressions are combined, C# needs a rule to decide which one to evaluate first. That rule is *operator precedence*. + +You don't need to memorize every detail of precedence. Three tiers cover nearly all everyday code, and when you're unsure, parentheses always make the order explicit and clear. ## Operator precedence -When an expression contains multiple operators, C# follows *operator precedence* rules to decide which operation to evaluate first — the same idea as the order-of-operations rules you learned in math class. +*Operator precedence* determines which part of a combined expression is evaluated first — exactly like the order-of-operations rules you learned in math class. -Memorizing three tiers covers most real code: +Three tiers cover most real code: 1. **Arithmetic first** — `*`, `/`, `%` bind tighter than `+` and `-`. Multiplication and division happen before addition and subtraction. 2. **Comparison next** — `<`, `>`, `<=`, `>=`, `==`, `!=` bind less tightly than arithmetic, so arithmetic completes before the comparison. 3. **Logical last** — `&&` and `||` bind least tightly of the common operators, so comparisons complete before the logical combination. -This means the expression `score + bonus > threshold && attempts < maxAttempts` evaluates exactly as you'd read it in English: add score and bonus, compare to threshold, compare attempts to maxAttempts, then combine the two `bool` results. +This means the expression `score + bonus > threshold && attempts < maxAttempts` evaluates exactly as you'd read it: add `score` and `bonus`, compare the sum to `threshold`, compare `attempts` to `maxAttempts`, then combine the two `bool` results with `&&`. The full operator precedence table — covering every operator — lives in the language reference: [C# operators and expressions](../../language-reference/operators/index.md). @@ -45,35 +49,41 @@ Parentheses override precedence and document your intent at the same time. When The last two lines show that parentheses can change the result, not just the style. When `&&` and `||` appear together, add parentheses to spell out which condition combines first. A reader who sees `(isAdmin && isOwner) || isSuperUser` knows the intent immediately. -## Operand evaluation order +## How expressions are evaluated -Regardless of precedence, C# evaluates the *operands* of most operators from left to right before applying the operator. Precedence determines which operator applies to which operands; left-to-right evaluation determines the order in which sub-expressions run. +Understanding how C# evaluates combined expressions is easier with an analogy. Think of working through a complex math problem with pencil and paper: you identify the innermost or highest-precedence sub-expression, compute its interim value, write down the result, then repeat with the next sub-expression — continuing until you reach the final answer. -```csharp -int a = 6; -int b = 2; -int c = 3; +C# follows the same process, guided by two rules: -// a / b is evaluated before + c, because / has higher precedence than + -int result = a / b + c; // (6 / 2) + 3 = 6 -``` +**Rule 1: Operands evaluate left to right.** For any binary expression, both operands must be fully evaluated before the operator is applied. C# evaluates the left operand first, then the right, then performs the operation. -This distinction rarely matters in everyday code. In practice, use parentheses or separate statements when you need a specific order. +**Rule 2: Some operators short-circuit.** Certain operators stop evaluating as soon as the result is determined, skipping any remaining operands: -## Associativity +- `&&` (conditional AND): returns `false` as soon as the left side is `false`. The right side is never evaluated. +- `||` (conditional OR): returns `true` as soon as the left side is `true`. The right side is never evaluated. +- `?:` (conditional/ternary): evaluates only the branch that matches the condition — the other branch is never evaluated. +- `?.` (null-conditional member access) and `?[]` (null-conditional element access): stop and return `null` immediately when the left side is `null`, skipping the member access or index. +- `??=` (null-coalescing assignment): assigns the right side only when the left side is `null`. -When two operators have the same precedence level, *associativity* decides which one applies first. Most C# operators are *left-associative*: `a - b - c` groups as `(a - b) - c`, working left to right. +### Paper-and-pencil evaluation -## Short-circuit evaluation +Consider the expression `a / b + c` with `a = 6`, `b = 2`, `c = 3`. Working through it step by step — just as you would on paper: -The logical operators `&&` (AND) and `||` (OR) are *short-circuit* operators: they stop evaluating as soon as the result is known. +:::code language="csharp" source="snippets/expressions/Program.cs" ID="StepByStep"::: -- `&&` returns `false` as soon as the left side is `false`. The right side is never evaluated. -- `||` returns `true` as soon as the left side is `true`. The right side is never evaluated. +`/` has higher precedence than `+`, so `a / b` is the first sub-expression. Its result, `3`, becomes the left operand of `+`. Then `3 + c` evaluates to `6`. + +*Associativity* is a related concept: when two operators have the same precedence, associativity decides which one goes first. Most C# operators are *left-associative*, meaning they group left to right. So `a - b - c` is the same as `(a - b) - c`, not `a - (b - c)`. + +### Short-circuit evaluation in practice + +Short-circuit evaluation is particularly useful for null checks: :::code language="csharp" source="snippets/expressions/Program.cs" ID="ShortCircuit"::: -Short-circuit evaluation is useful for null checks: `text != null && text.Length > 0` is safe because the second condition runs only when `text` is not null. For a broader look at null-safe operators, see [C# null operators](../null-safety/null-operators.md). +`text != null && text.Length > 0` is safe because the second condition runs only when `text` is not `null`. Similarly, `?.` stops evaluation when it encounters a `null` reference, which avoids a `NullReferenceException` without an explicit `if` check. + +For a broader look at null-safe operators, see [C# null operators](../null-safety/null-operators.md). ## See also diff --git a/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs b/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs index 2cef403d94b71..0aa94f9fdda16 100644 --- a/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs +++ b/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs @@ -27,6 +27,17 @@ Console.WriteLine(access2); // => False (false && anything is false) // +// +int a = 6; +int b = 2; +int c = 3; + +// Step 1: / has higher precedence than +, so evaluate a / b first → 3 +// Step 2: add the result to c → 3 + 3 = 6 +int result = a / b + c; // (6 / 2) + 3 = 6 +Console.WriteLine(result); // => 6 +// + // string? text = null; @@ -42,4 +53,9 @@ 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 // From ffe259e71f5752fe8c07c0d34f463aa27415965d Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 14 Aug 2026 14:21:06 -0400 Subject: [PATCH 05/11] Second-commit review: statement accuracy, precedence tier, paper-and-pencil walkthrough - Fix 'assignment statement' inaccuracy: explain int total = ... as a variable declaration statement whose initializer expression is evaluated and assigned - Add 'primary and unary first' tier to operator-precedence list, covering member access, method calls, indexing, null-conditional access, and unary operators; update tier count references throughout - Add explicit paper-and-pencil walkthrough using 3 + 6 / 2, showing each interim step (6/2 -> 3, then 3+3 -> 6) in both ASCII diagram and snippet Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9b155fe4-7996-4e57-ae91-d560587cc26d --- docs/csharp/fundamentals/expressions/index.md | 27 ++++++++++++------- .../snippets/expressions/Program.cs | 11 +++----- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/docs/csharp/fundamentals/expressions/index.md b/docs/csharp/fundamentals/expressions/index.md index acad83a485330..df13a0b6210b4 100644 --- a/docs/csharp/fundamentals/expressions/index.md +++ b/docs/csharp/fundamentals/expressions/index.md @@ -19,23 +19,24 @@ The simplest expressions are *literals* (like `42` or `"hello"`) and *variable n ### Expressions and statements -An expression produces a value. A *statement* is a complete instruction that the program executes — and many statements contain expressions. For example, in the statement `int total = 3 + 4 * 2;`, the expression `3 + 4 * 2` is evaluated first, and then the assignment statement stores the resulting value in `total`. This article focuses on expressions — how they're formed, how they're evaluated, and how they combine. +An expression produces a value. A *statement* is a complete instruction that the program executes. Many statements contain expressions. For example, `int total = 3 + 4 * 2;` is a variable declaration statement. The compiler evaluates the initializer expression `3 + 4 * 2`, which produces `11`, and assigns that value to the new variable `total`. This article focuses on expressions — how they're formed, how they're evaluated, and how they combine. ## Combining expressions Expressions can be combined. Consider `3 + 4 * 2`. This single expression actually contains two smaller expressions: the *multiplication expression* `4 * 2` and the *addition expression* `3 + `. When expressions are combined, C# needs a rule to decide which one to evaluate first. That rule is *operator precedence*. -You don't need to memorize every detail of precedence. Three tiers cover nearly all everyday code, and when you're unsure, parentheses always make the order explicit and clear. +You don't need to memorize every detail of precedence. Four tiers cover nearly all everyday code, and when you're unsure, parentheses always make the order explicit and clear. ## Operator precedence *Operator precedence* determines which part of a combined expression is evaluated first — exactly like the order-of-operations rules you learned in math class. -Three tiers cover most real code: +Four tiers cover most real code, ordered from tightest to loosest binding: -1. **Arithmetic first** — `*`, `/`, `%` bind tighter than `+` and `-`. Multiplication and division happen before addition and subtraction. -2. **Comparison next** — `<`, `>`, `<=`, `>=`, `==`, `!=` bind less tightly than arithmetic, so arithmetic completes before the comparison. -3. **Logical last** — `&&` and `||` bind least tightly of the common operators, so comparisons complete before the logical combination. +1. **Primary and unary first** — the tightest-binding operations: member access (`x.y`), method calls (`f()`), indexing (`a[i]`), null-conditional access (`?.`, `?[]`), and unary operators (`-x`, `!flag`, `++i`). These always apply before anything else. +2. **Arithmetic next** — `*`, `/`, `%` bind tighter than `+` and `-`. Multiplication and division happen before addition and subtraction. +3. **Comparison next** — `<`, `>`, `<=`, `>=`, `==`, `!=` bind less tightly than arithmetic, so arithmetic completes before the comparison. +4. **Logical last** — `&&` and `||` bind least tightly of the common operators, so comparisons complete before the logical combination. This means the expression `score + bonus > threshold && attempts < maxAttempts` evaluates exactly as you'd read it: add `score` and `bonus`, compare the sum to `threshold`, compare `attempts` to `maxAttempts`, then combine the two `bool` results with `&&`. @@ -43,7 +44,7 @@ The full operator precedence table — covering every operator — lives in the ### Use parentheses to make intent clear -Parentheses override precedence and document your intent at the same time. When the order isn't obvious from the three tiers above, add parentheses: +Parentheses override precedence and document your intent at the same time. When the order isn't obvious from the four tiers above, add parentheses: :::code language="csharp" source="snippets/expressions/Program.cs" ID="ParenthesesClarity"::: @@ -67,11 +68,19 @@ C# follows the same process, guided by two rules: ### Paper-and-pencil evaluation -Consider the expression `a / b + c` with `a = 6`, `b = 2`, `c = 3`. Working through it step by step — just as you would on paper: +Consider the expression `3 + 6 / 2`. Even though addition appears first in reading order, `/` has higher precedence than `+`, so `6 / 2` is the sub-expression that evaluates first. Working through it step by step — exactly as you would on paper: + +``` +3 + 6 / 2 + ↓ (evaluate 6 / 2 → 3) +3 + 3 + ↓ (evaluate 3 + 3 → 6) + 6 +``` :::code language="csharp" source="snippets/expressions/Program.cs" ID="StepByStep"::: -`/` has higher precedence than `+`, so `a / b` is the first sub-expression. Its result, `3`, becomes the left operand of `+`. Then `3 + c` evaluates to `6`. +The interim value `3` produced by `6 / 2` becomes the right operand of `+`, and the final result is `6`. Each sub-expression produces an interim value; those interim values feed the next sub-expression, until only one value remains. *Associativity* is a related concept: when two operators have the same precedence, associativity decides which one goes first. Most C# operators are *left-associative*, meaning they group left to right. So `a - b - c` is the same as `(a - b) - c`, not `a - (b - c)`. diff --git a/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs b/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs index 0aa94f9fdda16..1236fc773d165 100644 --- a/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs +++ b/docs/csharp/fundamentals/expressions/snippets/expressions/Program.cs @@ -28,13 +28,10 @@ // // -int a = 6; -int b = 2; -int c = 3; - -// Step 1: / has higher precedence than +, so evaluate a / b first → 3 -// Step 2: add the result to c → 3 + 3 = 6 -int result = a / b + c; // (6 / 2) + 3 = 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 // From f6e955cce46f16f6af0a0fe38bf6f563b2b12571 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 14 Aug 2026 14:23:45 -0400 Subject: [PATCH 06/11] Add range operator to precedence tier 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The range operator (..) sits in its own precedence band between Unary and Multiplicative in the C# spec table — above arithmetic, not below it. Expand tier 1 to 'Primary, unary, and range', naming all three groups and explaining what range does (slice expressions like array[1..4]). Note that each group has its own band while all three precede arithmetic, which is technically accurate and satisfies the pedagogical request. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9b155fe4-7996-4e57-ae91-d560587cc26d --- docs/csharp/fundamentals/expressions/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/fundamentals/expressions/index.md b/docs/csharp/fundamentals/expressions/index.md index df13a0b6210b4..ac5c8572b2fdb 100644 --- a/docs/csharp/fundamentals/expressions/index.md +++ b/docs/csharp/fundamentals/expressions/index.md @@ -33,7 +33,7 @@ You don't need to memorize every detail of precedence. Four tiers cover nearly a Four tiers cover most real code, ordered from tightest to loosest binding: -1. **Primary and unary first** — the tightest-binding operations: member access (`x.y`), method calls (`f()`), indexing (`a[i]`), null-conditional access (`?.`, `?[]`), and unary operators (`-x`, `!flag`, `++i`). These always apply before anything else. +1. **Primary, unary, and range** — all three groups bind more tightly than arithmetic. *Primary* operators include member access (`x.y`), method calls (`f()`), indexing (`a[i]`), and null-conditional access (`?.`, `?[]`). *Unary* operators act on a single operand: negation (`-x`), logical NOT (`!flag`), and prefix/postfix increment (`++i`). The *range* operator (`..`) builds index ranges for slice expressions, like `array[1..4]`. Each of these groups sits in its own precedence band, but all of them evaluate before any arithmetic. 2. **Arithmetic next** — `*`, `/`, `%` bind tighter than `+` and `-`. Multiplication and division happen before addition and subtraction. 3. **Comparison next** — `<`, `>`, `<=`, `>=`, `==`, `!=` bind less tightly than arithmetic, so arithmetic completes before the comparison. 4. **Logical last** — `&&` and `||` bind least tightly of the common operators, so comparisons complete before the logical combination. From 642dc8d5d9269fd3e66e8683d9a6663e54700279 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 14 Aug 2026 14:25:22 -0400 Subject: [PATCH 07/11] Fix increment example: show both prefix ++i and postfix i++ The previous wording said 'prefix/postfix increment (++i)' but ++i is only prefix. Separate them so each label matches its example. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9b155fe4-7996-4e57-ae91-d560587cc26d --- docs/csharp/fundamentals/expressions/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/fundamentals/expressions/index.md b/docs/csharp/fundamentals/expressions/index.md index ac5c8572b2fdb..75a269c3d90a3 100644 --- a/docs/csharp/fundamentals/expressions/index.md +++ b/docs/csharp/fundamentals/expressions/index.md @@ -33,7 +33,7 @@ You don't need to memorize every detail of precedence. Four tiers cover nearly a Four tiers cover most real code, ordered from tightest to loosest binding: -1. **Primary, unary, and range** — all three groups bind more tightly than arithmetic. *Primary* operators include member access (`x.y`), method calls (`f()`), indexing (`a[i]`), and null-conditional access (`?.`, `?[]`). *Unary* operators act on a single operand: negation (`-x`), logical NOT (`!flag`), and prefix/postfix increment (`++i`). The *range* operator (`..`) builds index ranges for slice expressions, like `array[1..4]`. Each of these groups sits in its own precedence band, but all of them evaluate before any arithmetic. +1. **Primary, unary, and range** — all three groups bind more tightly than arithmetic. *Primary* operators include member access (`x.y`), method calls (`f()`), indexing (`a[i]`), and null-conditional access (`?.`, `?[]`). *Unary* operators act on a single operand: negation (`-x`), logical NOT (`!flag`), prefix increment (`++i`), and postfix increment (`i++`). The *range* operator (`..`) builds index ranges for slice expressions, like `array[1..4]`. Each of these groups sits in its own precedence band, but all of them evaluate before any arithmetic. 2. **Arithmetic next** — `*`, `/`, `%` bind tighter than `+` and `-`. Multiplication and division happen before addition and subtraction. 3. **Comparison next** — `<`, `>`, `<=`, `>=`, `==`, `!=` bind less tightly than arithmetic, so arithmetic completes before the comparison. 4. **Logical last** — `&&` and `||` bind least tightly of the common operators, so comparisons complete before the logical combination. From 2514818c53953d648c54fc6facd8537c4337403d Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 14 Aug 2026 15:06:05 -0400 Subject: [PATCH 08/11] Define binding, clarify precedence groups, restore 'Primary, unary, and range' - Restore heading 'Primary, unary, and range' (Bill's chosen wording), superseding the local edit that had changed it to 'Primary and unary' - Add opening paragraph to Operator precedence defining what 'binding' means: how operators claim operands in a combined expression, explicitly kept distinct from runtime operand evaluation order (cross-linked to How expressions are evaluated) - Note that C# has more precedence groups than the four in the summary; the additional groups enforce familiar rules like multiplication before addition - Expand tier 1 to sub-bullets clearly stating that primary, unary, and range are three distinct precedence groups combined here because all bind before arithmetic - Replace full-table sentence with natural 'For the complete hierarchy...' close - Remove stale 'four tiers' wording from Combining expressions intro and from parentheses subsection; transitions now flow without redundant reassurances Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9b155fe4-7996-4e57-ae91-d560587cc26d --- docs/csharp/fundamentals/expressions/index.md | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/docs/csharp/fundamentals/expressions/index.md b/docs/csharp/fundamentals/expressions/index.md index 75a269c3d90a3..0a09971670ff0 100644 --- a/docs/csharp/fundamentals/expressions/index.md +++ b/docs/csharp/fundamentals/expressions/index.md @@ -25,26 +25,29 @@ An expression produces a value. A *statement* is a complete instruction that the Expressions can be combined. Consider `3 + 4 * 2`. This single expression actually contains two smaller expressions: the *multiplication expression* `4 * 2` and the *addition expression* `3 + `. When expressions are combined, C# needs a rule to decide which one to evaluate first. That rule is *operator precedence*. -You don't need to memorize every detail of precedence. Four tiers cover nearly all everyday code, and when you're unsure, parentheses always make the order explicit and clear. +You don't need to memorize the complete precedence hierarchy. The next section summarizes the everyday groups, and parentheses always let you make the order explicit when you're unsure. ## Operator precedence -*Operator precedence* determines which part of a combined expression is evaluated first — exactly like the order-of-operations rules you learned in math class. +*Operator precedence* describes how operators *bind* to their operands when expressions are combined. An operator that binds more tightly claims its operands before a lower-precedence operator can. In `3 + 4 * 2`, `*` binds more tightly than `+`, so it claims `4` and `2` as its operands first. The result of `4 * 2` then becomes an operand of `+`. Binding is about how the expression is structured — which operator applies to which sub-expressions — not about the runtime order in which values are computed (that's covered in [How expressions are evaluated](#how-expressions-are-evaluated)). -Four tiers cover most real code, ordered from tightest to loosest binding: +C# has more precedence groups than the four summarized here. Those additional groups enforce familiar rules — for example, multiplication before addition — and cover the complete set of operators. The following groups cover what you encounter most often in everyday code: -1. **Primary, unary, and range** — all three groups bind more tightly than arithmetic. *Primary* operators include member access (`x.y`), method calls (`f()`), indexing (`a[i]`), and null-conditional access (`?.`, `?[]`). *Unary* operators act on a single operand: negation (`-x`), logical NOT (`!flag`), prefix increment (`++i`), and postfix increment (`i++`). The *range* operator (`..`) builds index ranges for slice expressions, like `array[1..4]`. Each of these groups sits in its own precedence band, but all of them evaluate before any arithmetic. -2. **Arithmetic next** — `*`, `/`, `%` bind tighter than `+` and `-`. Multiplication and division happen before addition and subtraction. -3. **Comparison next** — `<`, `>`, `<=`, `>=`, `==`, `!=` bind less tightly than arithmetic, so arithmetic completes before the comparison. -4. **Logical last** — `&&` and `||` bind least tightly of the common operators, so comparisons complete before the logical combination. +1. **Primary, unary, and range** — these are three distinct precedence groups, all of which bind more tightly than arithmetic. This summary combines them into one step because, for practical purposes, they all apply before arithmetic does. + - *Primary* operators — member access (`x.y`), method calls (`f()`), indexing (`a[i]`), and null-conditional access (`?.`, `?[]`) — bind most tightly of all. + - *Unary* operators act on a single operand: negation (`-x`), logical NOT (`!flag`), prefix increment (`++i`), and postfix increment (`i++`). + - The *range* operator (`..`) builds index ranges for slice expressions, like `array[1..4]`. +2. **Arithmetic** — `*`, `/`, `%` bind more tightly than `+` and `-`. Multiplication and division happen before addition and subtraction. +3. **Comparison** — `<`, `>`, `<=`, `>=`, `==`, `!=` bind less tightly than arithmetic, so arithmetic completes before the comparison. +4. **Logical** — `&&` and `||` bind least tightly of the common operators, so comparisons complete before the logical combination. This means the expression `score + bonus > threshold && attempts < maxAttempts` evaluates exactly as you'd read it: add `score` and `bonus`, compare the sum to `threshold`, compare `attempts` to `maxAttempts`, then combine the two `bool` results with `&&`. -The full operator precedence table — covering every operator — lives in the language reference: [C# operators and expressions](../../language-reference/operators/index.md). +For the complete precedence hierarchy covering every operator, see [C# operators and expressions](../../language-reference/operators/index.md). ### Use parentheses to make intent clear -Parentheses override precedence and document your intent at the same time. When the order isn't obvious from the four tiers above, add parentheses: +Parentheses override precedence and document your intent at the same time. When the order isn't obvious from the groups above, add parentheses: :::code language="csharp" source="snippets/expressions/Program.cs" ID="ParenthesesClarity"::: From ae5908c723439b5d53e8e7647e06bb32cbf8dca3 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 14 Aug 2026 15:17:17 -0400 Subject: [PATCH 09/11] Show parenthesized form of precedence example on line 44 After the prose walk-through of score + bonus > threshold && attempts < maxAttempts, add the same expression with explicit grouping parentheses: ((score + bonus) > threshold) && (attempts < maxAttempts) This reinforces the precedence explanation before the parentheses subsection without duplicating its distinct content (which uses different variables and focuses on cases where parentheses change the result). Preserves the minor prose tweaks already present as local edits. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9b155fe4-7996-4e57-ae91-d560587cc26d --- docs/csharp/fundamentals/expressions/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/csharp/fundamentals/expressions/index.md b/docs/csharp/fundamentals/expressions/index.md index 0a09971670ff0..8f709c45bd14c 100644 --- a/docs/csharp/fundamentals/expressions/index.md +++ b/docs/csharp/fundamentals/expressions/index.md @@ -29,11 +29,11 @@ You don't need to memorize the complete precedence hierarchy. The next section s ## Operator precedence -*Operator precedence* describes how operators *bind* to their operands when expressions are combined. An operator that binds more tightly claims its operands before a lower-precedence operator can. In `3 + 4 * 2`, `*` binds more tightly than `+`, so it claims `4` and `2` as its operands first. The result of `4 * 2` then becomes an operand of `+`. Binding is about how the expression is structured — which operator applies to which sub-expressions — not about the runtime order in which values are computed (that's covered in [How expressions are evaluated](#how-expressions-are-evaluated)). +*Operator precedence* describes how operators *bind* to their operands when you combine expressions. An operator that binds more tightly claims its operands before a lower-precedence operator can. In `3 + 4 * 2`, `*` binds more tightly than `+`, so it claims `4` and `2` as its operands first. The result of `4 * 2` then becomes an operand of `+`. Binding is about how the expression is structured — which operator applies to which sub-expressions — not about the runtime order in which values are computed. For runtime order, see [How expressions are evaluated](#how-expressions-are-evaluated). C# has more precedence groups than the four summarized here. Those additional groups enforce familiar rules — for example, multiplication before addition — and cover the complete set of operators. The following groups cover what you encounter most often in everyday code: -1. **Primary, unary, and range** — these are three distinct precedence groups, all of which bind more tightly than arithmetic. This summary combines them into one step because, for practical purposes, they all apply before arithmetic does. +1. **Primary, unary, and range** — these are three distinct precedence groups, all of which bind more tightly than arithmetic. This summary combines them into one step because, for practical purposes, they all apply before arithmetic. - *Primary* operators — member access (`x.y`), method calls (`f()`), indexing (`a[i]`), and null-conditional access (`?.`, `?[]`) — bind most tightly of all. - *Unary* operators act on a single operand: negation (`-x`), logical NOT (`!flag`), prefix increment (`++i`), and postfix increment (`i++`). - The *range* operator (`..`) builds index ranges for slice expressions, like `array[1..4]`. @@ -41,7 +41,7 @@ C# has more precedence groups than the four summarized here. Those additional gr 3. **Comparison** — `<`, `>`, `<=`, `>=`, `==`, `!=` bind less tightly than arithmetic, so arithmetic completes before the comparison. 4. **Logical** — `&&` and `||` bind least tightly of the common operators, so comparisons complete before the logical combination. -This means the expression `score + bonus > threshold && attempts < maxAttempts` evaluates exactly as you'd read it: add `score` and `bonus`, compare the sum to `threshold`, compare `attempts` to `maxAttempts`, then combine the two `bool` results with `&&`. +This precedence means the expression `score + bonus > threshold && attempts < maxAttempts` evaluates exactly as you'd read it: add `score` and `bonus`, compare the sum to `threshold`, compare `attempts` to `maxAttempts`, then combine the two `bool` results with `&&`. Adding parentheses to make every grouping explicit shows the same structure: `((score + bonus) > threshold) && (attempts < maxAttempts)`. For the complete precedence hierarchy covering every operator, see [C# operators and expressions](../../language-reference/operators/index.md). From e2e96778515ab6714d42c7e1c863df5e599e49ed Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 14 Aug 2026 15:19:28 -0400 Subject: [PATCH 10/11] Rewrite Operator precedence opening paragraph to remove confusing contrast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous paragraph defined precedence via 'binding order' then immediately disclaimed that binding isn't runtime order — confusing for beginners. Rewrite uses only structural language: - Precedence determines how a combined expression *groups* into sub-expressions - 'Binds more tightly' means the expression is structured as if parenthesized - Concrete example: 3 + 4 * 2 groups as 3 + (4 * 2), not (3 + 4) * 2 No mention of runtime evaluation order in this paragraph. The 'How expressions are evaluated' section owns that concept. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9b155fe4-7996-4e57-ae91-d560587cc26d --- docs/csharp/fundamentals/expressions/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/fundamentals/expressions/index.md b/docs/csharp/fundamentals/expressions/index.md index 8f709c45bd14c..d82b2374e416e 100644 --- a/docs/csharp/fundamentals/expressions/index.md +++ b/docs/csharp/fundamentals/expressions/index.md @@ -29,7 +29,7 @@ You don't need to memorize the complete precedence hierarchy. The next section s ## Operator precedence -*Operator precedence* describes how operators *bind* to their operands when you combine expressions. An operator that binds more tightly claims its operands before a lower-precedence operator can. In `3 + 4 * 2`, `*` binds more tightly than `+`, so it claims `4` and `2` as its operands first. The result of `4 * 2` then becomes an operand of `+`. Binding is about how the expression is structured — which operator applies to which sub-expressions — not about the runtime order in which values are computed. For runtime order, see [How expressions are evaluated](#how-expressions-are-evaluated). +*Operator precedence* determines how a combined expression *groups* into sub-expressions — the same idea as the order-of-operations rules from math class. An operator with higher precedence *binds more tightly* to its neighboring operands, meaning the expression is structured as if those operands are parenthesized together. In `3 + 4 * 2`, `*` has higher precedence than `+`, so the expression groups as `3 + (4 * 2)`, not `(3 + 4) * 2`. C# has more precedence groups than the four summarized here. Those additional groups enforce familiar rules — for example, multiplication before addition — and cover the complete set of operators. The following groups cover what you encounter most often in everyday code: From 15756f41decacfddefed025b754a685570e0ac90 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 14 Aug 2026 15:34:19 -0400 Subject: [PATCH 11/11] One more proofread --- docs/csharp/fundamentals/expressions/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/csharp/fundamentals/expressions/index.md b/docs/csharp/fundamentals/expressions/index.md index d82b2374e416e..d36c29dae0f52 100644 --- a/docs/csharp/fundamentals/expressions/index.md +++ b/docs/csharp/fundamentals/expressions/index.md @@ -29,7 +29,7 @@ You don't need to memorize the complete precedence hierarchy. The next section s ## Operator precedence -*Operator precedence* determines how a combined expression *groups* into sub-expressions — the same idea as the order-of-operations rules from math class. An operator with higher precedence *binds more tightly* to its neighboring operands, meaning the expression is structured as if those operands are parenthesized together. In `3 + 4 * 2`, `*` has higher precedence than `+`, so the expression groups as `3 + (4 * 2)`, not `(3 + 4) * 2`. +*Operator precedence* determines how a combined expression *groups* into sub-expressions. This concept is similar to the order-of-operations rules from math class. An operator with higher precedence *binds more tightly* to its operands. The expression is structured as if those operands are parenthesized together. In `3 + 4 * 2`, `*` has higher precedence than `+`, so the expression groups as `3 + (4 * 2)`, not `(3 + 4) * 2`. C# has more precedence groups than the four summarized here. Those additional groups enforce familiar rules — for example, multiplication before addition — and cover the complete set of operators. The following groups cover what you encounter most often in everyday code: @@ -93,7 +93,7 @@ Short-circuit evaluation is particularly useful for null checks: :::code language="csharp" source="snippets/expressions/Program.cs" ID="ShortCircuit"::: -`text != null && text.Length > 0` is safe because the second condition runs only when `text` is not `null`. Similarly, `?.` stops evaluation when it encounters a `null` reference, which avoids a `NullReferenceException` without an explicit `if` check. +`text != null && text.Length > 0` is safe because the second condition runs only when `text` isn't `null`. Similarly, `?.` stops evaluation when it encounters a `null` reference, which avoids a `NullReferenceException` without an explicit `if` check. For a broader look at null-safe operators, see [C# null operators](../null-safety/null-operators.md).