C# Operators

Operators are symbols and keywords that tell C# to perform an action on one or more values. They let you add numbers, compare values, combine Boolean conditions, assign results, handle null, and choose between alternatives. Understanding operators matters because small details such as integer division, short-circuiting, and precedence can completely change what a program does.

Overview: How Operators Work

An operator works with operands. In a + b, the operator is +, and a and b are operands. Some operators are unary, such as -number or !isReady. Most are binary, such as total * rate. The conditional operator condition ? whenTrue : whenFalse is ternary because it has three operands.

C# is strongly typed, so the compiler decides which operator meaning applies from the operand types. The + operator adds numbers, but it concatenates strings. == compares numeric values by value, compares bool values by value, and for many reference types checks whether two references point to the same object unless the type overloads equality. Strings are a special common case: == compares string contents.

Operators also participate in numeric promotion. For example, arithmetic on byte, short, and char usually produces an int. If either side of an arithmetic expression is double, the other side is converted to double for that expression. Integer division is another key rule: 7 / 2 produces 3, not 3.5, because both operands are integers.

The compiler emits Intermediate Language instructions or method calls for operators, then the CLR executes them. Built-in numeric and Boolean operators map to efficient runtime instructions. User-defined types can overload many operators, which means money1 + money2 can call a method written by the type author. Even then, the compiler still checks whether the operator exists and whether the result type can be assigned where you put it.

C# also has operator precedence and associativity. Precedence decides which operator groups first, as multiplication does before addition in 2 + 3 * 4. Associativity decides how operators of the same precedence group. Parentheses are the clearest way to state your intention and should be used whenever the default grouping is not immediately obvious.

Syntax

int a = 8;
int b = 3;
int age = 20;
bool hasTicket = true;
string? name = null;
int score = 72;
int flags = 2;
int sum = a + b;
int remainder = a % b;
a += 5;
a++;
bool allowed = age >= 18 && hasTicket;
string label = name ?? "Guest";
string result = score >= 60 ? "Pass" : "Retry";
int shifted = flags << 1;
Category Operators Purpose
Arithmetic + - * / % Add, subtract, multiply, divide, and get a remainder.
Assignment = += -= *= /= %= Store or update a variable.
Increment ++ -- Increase or decrease by one, before or after reading the value.
Comparison == != < > <= >= Produce a bool by comparing values.
Logical && || ! Combine or invert Boolean expressions, with short-circuit behavior.
Null handling ?? ??= ?. Use fallback values, assign defaults, or safely access members.
Conditional ?: Choose one of two expressions from a condition.
Bitwise & | ^ ~ << >> Work with individual bits in integer values.

Examples

Arithmetic, Division, and Remainders

using System;

class Program
{
    static void Main()
    {
        int items = 17;
        int boxes = 5;

        int fullBoxes = items / boxes;
        int leftover = items % boxes;
        double exactBoxes = items / (double)boxes;

        Console.WriteLine($"Full boxes: {fullBoxes}");
        Console.WriteLine($"Left over: {leftover}");
        Console.WriteLine($"Exact boxes: {exactBoxes}");
    }
}

Output:

Full boxes: 3
Left over: 2
Exact boxes: 3.4

Because items and boxes are both int, items / boxes uses integer division. The remainder operator % gives what is left after making full groups. Casting one operand to double changes the division to floating-point division for that expression.

Comparison and Logical Operators

using System;

class Program
{
    static void Main()
    {
        int age = 20;
        bool hasTicket = true;
        bool isSuspended = false;

        bool canEnter = age >= 18 && hasTicket && !isSuspended;
        bool needsHelp = age < 18 || !hasTicket;

        Console.WriteLine($"Can enter: {canEnter}");
        Console.WriteLine($"Needs help: {needsHelp}");
    }
}

Output:

Can enter: True
Needs help: False

Comparison operators such as >= and < produce bool values. && means both sides must be true, || means at least one side must be true, and ! flips a Boolean value. These operators short-circuit: with &&, C# skips the right side when the left side is false; with ||, it skips the right side when the left side is true.

Assignment, Increment, and Precedence

using System;

class Program
{
    static void Main()
    {
        int count = 3;
        int firstRead = count++;
        int secondRead = ++count;

        int total = 2 + 3 * 4;
        int grouped = (2 + 3) * 4;

        count += 10;

        Console.WriteLine($"First read: {firstRead}");
        Console.WriteLine($"Second read: {secondRead}");
        Console.WriteLine($"Count: {count}");
        Console.WriteLine($"Total: {total}");
        Console.WriteLine($"Grouped: {grouped}");
    }
}

Output:

First read: 3
Second read: 5
Count: 15
Total: 14
Grouped: 20

count++ returns the old value, then increments. ++count increments first, then returns the new value. Multiplication has higher precedence than addition, so 2 + 3 * 4 is 14. Parentheses in (2 + 3) * 4 force the addition to happen first.

Null and Conditional Operators

using System;

class Program
{
    static void Main()
    {
        string? nickname = null;
        string displayName = nickname ?? "Anonymous";

        string? message = "hello";
        int? length = message?.Length;

        int score = 82;
        string status = score >= 60 ? "Pass" : "Retry";

        Console.WriteLine(displayName);
        Console.WriteLine(length);
        Console.WriteLine(status);
    }
}

Output:

Anonymous
5
Pass

The null-coalescing operator ?? returns its left operand when it is not null, otherwise it returns the fallback. The null-conditional operator ?. accesses a member only when the receiver is not null; otherwise the whole expression becomes null. The conditional operator chooses between two expressions based on a Boolean condition.

How Operators Work Step by Step

  1. The compiler gives every literal, variable, and expression a compile-time type.
  2. It uses the operand types to find the applicable built-in or overloaded operator.
  3. If needed, it applies allowed numeric conversions, such as converting an int to double when combined with a double.
  4. It groups expressions according to precedence and associativity, unless parentheses override that grouping.
  5. For &&, ||, ??, and ?., it emits branching logic so later operands may not be evaluated.
  6. At runtime, the CLR executes the generated instructions and stores the result in the target variable, passes it to a method, or uses it to decide a branch.

The difference between eager and short-circuit evaluation is important. & and | can work with Boolean operands, but they evaluate both sides. && and || are usually preferred for conditions because they skip unnecessary work and avoid errors such as calling a method on a null value after a failed left-side check.

Common Mistakes

Expecting Integer Division to Keep Decimals

int result = 7 / 2;
Console.WriteLine(result);

Output:

3

This compiles, but it may not be the answer you meant. If the result should include a fractional part, make at least one operand a floating-point type.

double result = 7 / 2.0;
Console.WriteLine(result);

Output:

3.5

Using Assignment Instead of Equality

int score = 100;
if (score = 100)
{
    Console.WriteLine("Perfect");
}

This does not compile because score = 100 is an assignment expression with type int, but if requires a bool. Use == for equality comparisons.

int score = 100;
if (score == 100)
{
    Console.WriteLine("Perfect");
}

Output:

Perfect

Treating Null-Conditional Access as a Boolean

string? name = null;
if (name?.Length)
{
    Console.WriteLine(name);
}

This does not compile because name?.Length is an int?, not a bool. Compare the length or use ?? to provide a numeric fallback.

string? name = null;
if ((name?.Length ?? 0) > 0)
{
    Console.WriteLine(name);
}
else
{
    Console.WriteLine("Missing name");
}

Output:

Missing name

Best Practices

  • Use parentheses when mixing several operators and the default precedence is not obvious to a reader.
  • Use && and || for normal Boolean conditions; reserve & and | for bitwise work or deliberate eager Boolean evaluation.
  • Be explicit about numeric types when division should produce decimals, such as total / 100.0.
  • Avoid packing too much logic into one expression. A well-named Boolean variable often makes a complex condition easier to test and maintain.
  • Use ??, ??=, and ?. to express null handling directly instead of writing repetitive nested if statements.
  • Do not rely on prefix and postfix increment side effects inside large expressions. Split the statement when order matters.
  • Use decimal for financial calculations and keep rounding rules explicit.
  • When comparing strings for user-facing rules, consider whether case sensitivity and culture matter; == is ordinal for strings.
  • Use bitwise operators only when the values represent flags, masks, or low-level binary data.

Practice Exercises

  1. Write a program with int minutes = 135;. Use / and % to print full hours and remaining minutes.
  2. Create three Boolean variables: isMember, hasCoupon, and cartTotal as a number. Build a condition that gives a discount when the customer is a member or has a coupon, but only when the cart total is at least 25.
  3. Declare a nullable string variable. Use ?? to print a fallback value, then change the variable and use ?.Length to print its length safely.

Summary

  • Operators combine, compare, assign, and transform values in C# expressions.
  • The operand types determine which operator meaning is used and what result type is produced.
  • Integer division drops the fractional part; use a floating-point or decimal operand when you need decimals.
  • &&, ||, ??, and ?. can skip later evaluation, which affects both performance and safety.
  • Precedence controls grouping, but parentheses are often clearer than asking readers to remember a table.
  • ++x and x++ both increment, but they return different values.
  • The null and conditional operators help keep common decision-making expressions compact without giving up type safety.