← Back to C# series
🟣
Basics
Basics Β· Prerequisite: previous lecture

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.

C#.NET 8basicsoperators
Duration
⏱ ~1-1.5 hours
Level
πŸ“Š Beginner
Prerequisite
🎯 Previous lecture or equivalent
OUTCOME
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**.

csharp
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`.

csharp
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`
csharp
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
csharp
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 == null

5) Ternary operator `?:`

"condition ? trueValue : falseValue" β€” a short if-else as an expression.

csharp
int score = 75;
string grade = score >= 60 ? "Pass" : "Fail";

Examples

Example 1 β€” `Arithmetic/Program.cs`: integer vs real division

csharp
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**

text
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

csharp
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**

text
(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`: `?.` `??` `??=`

csharp
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**

text
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

csharp
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**

text
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

xml
<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

csharp
// 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

xml
<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

csharp
// 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

xml
<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

csharp
// && / || 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

xml
<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

csharp
// ?. ?? ??= 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

  1. Expecting `7 / 2` to be `3.5` β€” integer-to-integer is integer division.
  2. 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.
  3. Mixing up `??` and `?:` β€” `??` is **for null**, `?:` is a general condition.
  4. Using `as` with value types β€” `as int` is invalid; use `as int?`.
  5. 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

text
Height (cm): 172
Weight (kg): 70
BMI: 23.66

Hints

  • 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

text
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

xml
<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

csharp
// 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

xml
<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

csharp
// 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

bash
cd src/Arithmetic && dotnet run
cd ../LogicalOps && dotnet run
cd ../NullSafety && dotnet run
cd ../IsAs && dotnet run

Next Lecture

[04_Control_Flow](../04_%EC%A0%9C%EC%96%B4%EB%AC%B8/) β€” Control program flow with conditions and loops.

Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub β†—