Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The -- operator is a unary decrement operator that subtracts 1 (or the byte size of the type, in the case of pointers) from the value of its operand. It mutates the operand in place and requires the operand to be a variable, a property access, or an indexer access. The operator operates in two distinct evaluation modes depending on its placement relative to the operand: prefix and postfix.

Prefix Decrement (--x)

In prefix form, the operator decrements the operand’s value and evaluates to the new mutated value. The decrement operation occurs before the value is yielded to the enclosing expression.
int x = 5;
int y = --x; 
// Evaluation sequence:
// 1. x is decremented to 4.
// 2. The expression '--x' evaluates to 4.
// 3. y is assigned 4.

Postfix Decrement (x--)

In postfix form, the operator evaluates to the original value of the operand, and then decrements the operand. The original value is temporarily cached for the enclosing expression while the underlying variable is mutated.
int x = 5;
int y = x--; 
// Evaluation sequence:
// 1. The original value of x (5) is cached.
// 2. x is decremented to 4.
// 3. The expression 'x--' evaluates to the cached value (5).
// 4. y is assigned 5.

Type Support and Semantics

The -- operator is natively supported by the following types:
  • Integral types: sbyte, byte, short, ushort, int, uint, long, ulong, nint, nuint
  • Floating-point types: float, double (subtracts 1.0 from the operand)
  • High-precision decimal: decimal
  • Characters: char (decrements the underlying UTF-16 code point)
  • Enumerations: enum (decrements the underlying integral type)
  • Pointer types: T* (in an unsafe context, subtracts sizeof(T) bytes from the memory address)

Arithmetic Overflow

When applied to integral types, the behavior of the -- operator upon decrementing past the minimum representable value of the type depends on the execution context:
  • unchecked context (default): The operation silently underflows and wraps around to the maximum representable value of the type.
  • checked context: The runtime throws a System.OverflowException.
byte b1 = 0;
unchecked
{
    b1--; // b1 wraps around to 255
}

byte b2 = 0;
checked
{
    b2--; // Throws System.OverflowException
}

Operator Overloading

Custom class and struct types can overload the -- operator. When overloading, you only define the operator once; the C# compiler automatically handles the distinction between prefix and postfix evaluation based on the call site.
public struct Vector1D
{
    public int X { get; set; }

    public static Vector1D operator --(Vector1D v)
    {
        return new Vector1D { X = v.X - 1 };
    }
}

Execution Mechanics and Thread Safety

At the Intermediate Language (IL) level, the -- operator is not a single atomic instruction. It expands into a read-modify-write sequence:
  1. Load the value from memory.
  2. Subtract 1 (or the appropriate offset).
  3. Store the new value back to memory.
Because of this multi-step execution, the -- operator is not thread-safe. If multiple threads concurrently apply the -- operator to the same shared variable, race conditions will occur. For atomic decrements in multithreaded scenarios, the System.Threading.Interlocked.Decrement method must be used instead.
Master C# with Deep Grasping Methodology!Learn More