03. Operators and Expressions
Operators combine values to produce new ones. This lecture covers arithmetic, comparison, logical, assignment, increment/decrement, null-coalescing, ternary, and bitwise operators.
What you'll learn
- 1Know the precedence of arithmetic / comparison / logical operators
- 2Understand the difference between `int / int` and `double / int`
- 3Know **short-circuit evaluation** for `&&` and `||`
- 4Handle null safely with `?.` `??` `??=`
- 5Use `is` / `as` for type checks and conversions
- 6Use the ternary operator `?:` to write expressions concisely
Overview
Operators are the basic tool for transforming values. Beyond standard arithmetic/comparison/logic, C# offers **null-safe operators**, **pattern matching (`is`)**, and the **ternary operator (`?:`)** for expression-oriented code.
Core Concepts
1) Arithmetic operators
`+`, `-`, `*`, `/`, `%` are available. The key is the **type rules**.
int x = 7 / 2; // 3 (integer division, truncated)
double y = 7 / 2; // 3.0 (integer division first, then converted to double)
double z = 7.0 / 2; // 3.5 (if either side is double, real-number division)
int r = 7 % 2; // 1 (remainder)2) Comparison / logical operators
- Comparison: `==`, `!=`, `<`, `<=`, `>`, `>=` β returns `bool`
- Logical: `&&` (AND), `||` (OR), `!` (NOT)
**Short-circuit evaluation**: `&&` skips the right side if the left is `false`. `||` skips the right side if the left is `true`.
int? n = null;
if (n != null && n.Value > 0) // if n == null, .Value isn't evaluated
Console.WriteLine("positive");3) null-conditional `?.` and null-coalescing `??`, `??=`
- `obj?.Member`: if `obj` is `null`, the whole expression is `null`, otherwise evaluate `Member`
- `a ?? b`: `b` if `a` is `null`
- `a ??= b`: assign `b` only when `a` is `null`
string? name = null;
int len = name?.Length ?? 0; // 0 if null
name ??= "(no name)"; // assign only when null
Console.WriteLine($"{name} ({len})"); // (no name) (0)4) `is` and `as`
- `obj is Type`: `true` if the type matches, combines with pattern matching
- `obj as Type`: returns **`null` instead of throwing** if the cast fails
object o = "hello";
if (o is string s) // type check + variable declaration
Console.WriteLine(s.Length); // 5
object o2 = 42;
string? s2 = o2 as string; // cast fails β s2 == null5) Ternary operator `?:`
"condition ? trueValue : falseValue" β a short if-else as an expression.
int score = 75;
string grade = score >= 60 ? "Pass" : "Fail";Examples
Example 1 β `Arithmetic/Program.cs`: integer vs real division
int a = 7, b = 2;
Console.WriteLine($"{a} + {b} = {a + b}");
Console.WriteLine($"{a} - {b} = {a - b}");
Console.WriteLine($"{a} * {b} = {a * b}");
Console.WriteLine($"{a} / {b} = {a / b} (integer division)");
Console.WriteLine($"{a} % {b} = {a % b}");
Console.WriteLine($"(double)a / b = {(double)a / b} (real division)");**Output**
7 + 2 = 9
7 - 2 = 5
7 * 2 = 14
7 / 2 = 3 (integer division)
7 % 2 = 1
(double)a / b = 3.5 (real division)**Note:** Casting just one side with `(double)` makes the whole result a `double`.
Example 2 β `LogicalOps/Program.cs`: short-circuit demo
static bool Log(string label, bool value)
{
Console.WriteLine($" [{label} evaluated β {value}]");
return value;
}
Console.WriteLine("(false && X) result:");
bool r1 = Log("A", false) && Log("B", true);
Console.WriteLine($" => {r1}");
Console.WriteLine("(true || X) result:");
bool r2 = Log("C", true) || Log("D", false);
Console.WriteLine($" => {r2}");**Output**
(false && X) result:
[A evaluated β False]
=> False
(true || X) result:
[C evaluated β True]
=> True**Note:** `B` and `D` are not evaluated. That's short-circuit evaluation.
Example 3 β `NullSafety/Program.cs`: `?.` `??` `??=`
string? name = null;
int len = name?.Length ?? 0; // 0 if null
Console.WriteLine($"Length: {len}");
name ??= "(no name)"; // assign only when null
Console.WriteLine($"Name: {name}");
// once more: now it's not null, so nothing changes
name ??= "another default";
Console.WriteLine($"Final: {name}");**Output**
Length: 0
Name: (no name)
Final: (no name)**Note:** `?.` makes the **whole expression null** when something is null. That's why it pairs naturally with `??`.
Example 4 β `IsAs/Program.cs`: type checks and casts
object[] items = ["hello", 42, 3.14, true];
foreach (object item in items)
{
if (item is string s)
Console.WriteLine($"string: '{s}' (length {s.Length})");
else if (item is int n)
Console.WriteLine($"int: {n}");
else
Console.WriteLine($"other: {item} ({item.GetType().Name})");
}
object boxed = 100;
string? wrong = boxed as string; // cast fails β null
Console.WriteLine($"as result: {wrong ?? "(null)"}");**Output**
string: 'hello' (length 5)
int: 42
other: 3.14 (Double)
other: True (Boolean)
as result: (null)**Note:** The `is Type var` pattern is the cleanest. A forced cast like `(string)boxed` throws on failure, so use `as` or `is` for safe access.
Full example code (src/)
src/Arithmetic/Arithmetic.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.Basics03</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
src/Arithmetic/Program.cs
// arithmetic - integer vs real division
int a = 7, b = 2;
Console.WriteLine($"{a} + {b} = {a + b}");
Console.WriteLine($"{a} - {b} = {a - b}");
Console.WriteLine($"{a} * {b} = {a * b}");
Console.WriteLine($"{a} / {b} = {a / b} (integer division)");
Console.WriteLine($"{a} % {b} = {a % b}");
Console.WriteLine($"(double)a / b = {(double)a / b} (real division)");
src/IsAs/IsAs.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.Basics03</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
src/IsAs/Program.cs
// is / as type check and cast
object[] items = ["hello", 42, 3.14, true];
foreach (object item in items)
{
if (item is string s)
Console.WriteLine($"string: '{s}' (length {s.Length})");
else if (item is int n)
Console.WriteLine($"int: {n}");
else
Console.WriteLine($"other: {item} ({item.GetType().Name})");
}
object boxed = 100;
string? wrong = boxed as string; // cast fails β null
Console.WriteLine($"as result: {wrong ?? "(null)"}");
src/LogicalOps/LogicalOps.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.Basics03</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
src/LogicalOps/Program.cs
// && / || short-circuit demo
static bool Log(string label, bool value)
{
Console.WriteLine($" [{label} evaluated β {value}]");
return value;
}
Console.WriteLine("(false && X) result:");
bool r1 = Log("A", false) && Log("B", true);
Console.WriteLine($" => {r1}");
Console.WriteLine("(true || X) result:");
bool r2 = Log("C", true) || Log("D", false);
Console.WriteLine($" => {r2}");
src/NullSafety/NullSafety.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.Basics03</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
src/NullSafety/Program.cs
// ?. ?? ??= demo
string? name = null;
int len = name?.Length ?? 0; // 0 if null
Console.WriteLine($"Length: {len}");
name ??= "(no name)"; // assign only when null
Console.WriteLine($"Name: {name}");
// already has a value, so this does nothing
name ??= "another default";
Console.WriteLine($"Final: {name}");
Common Mistakes
- Expecting `7 / 2` to be `3.5` β integer-to-integer is integer division.
- Confusing `=` and `==` like `if (count = 0)` β C# won't accept a non-bool expression in `if`, so this is at least caught at compile time, but be careful.
- Mixing up `??` and `?:` β `??` is **for null**, `?:` is a general condition.
- Using `as` with value types β `as int` is invalid; use `as int?`.
- Accessing `.` on a `null` object β `NullReferenceException`. **Always consider `?.`**.
Summary
- Distinguish integer division from real division
- `&&`/`||` short-circuiting safely chains null checks and other tests
- `?.` `??` `??=` is the trio for null handling
- `is`/`as` for safe type checks and conversions
- Ternary `?:` expresses short branches cleanly
Practice
**Practice - 03. Operators and Expressions**
Problem 1 β BMI calculator
- Project folder: `Homework01/`
- Key concepts: `double` math, comparison, `:F2` string interpolation
Requirements
- Read height (cm) and weight (kg)
- Compute BMI = `weight / (heightM Γ heightM)`
- Print to two decimal places (`{bmi:F2}`)
Expected output
Height (cm): 172
Weight (kg): 70
BMI: 23.66Hints
- Convert cm to m with `/ 100.0` (use `100.0` to guarantee `double`)
- Read a real number with `double.Parse(Console.ReadLine()!)`
Problem 2 β Even/odd check (ternary operator)
- Project folder: `Homework02/`
- Key concepts: `%`, ternary `?:`
Requirements
- Read one integer
- Use the ternary operator to decide even or odd and print
- For 0 print "0 is even" (treat 0 as even)
Expected output
Integer: 7
7 is odd.Hints
- `n % 2 == 0` means even
- `string result = ... ? "even" : "odd";`
Check your answer
Try it yourself, then compare against the [`answer/`](./answer/) folder.
Answer (answer/)
homework/answer/Homework01/Homework01.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.Basics03</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
homework/answer/Homework01/Program.cs
// BMI calculator
Console.Write("Height (cm): ");
double heightCm = double.Parse(Console.ReadLine()!);
Console.Write("Weight (kg): ");
double weightKg = double.Parse(Console.ReadLine()!);
double heightM = heightCm / 100.0;
double bmi = weightKg / (heightM * heightM);
Console.WriteLine($"BMI: {bmi:F2}");
homework/answer/Homework02/Homework02.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>CodingNow.Lecture.Basics03</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
homework/answer/Homework02/Program.cs
// even/odd via ternary
Console.Write("Integer: ");
int n = int.Parse(Console.ReadLine()!);
string kind = n % 2 == 0 ? "even" : "odd";
Console.WriteLine($"{n} is {kind}.");
Try It Yourself
cd src/Arithmetic && dotnet run
cd ../LogicalOps && dotnet run
cd ../NullSafety && dotnet run
cd ../IsAs && dotnet runNext Lecture
[04_Control_Flow](../04_%EC%A0%9C%EC%96%B4%EB%AC%B8/) β Control program flow with conditions and loops.
All lecture materials and example code are openly available on GitHub.
View on GitHub β