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 arithmetic operator that decrements the value of its operand by 1. It mutates the operand in place and is supported by all built-in integral and floating-point numeric types, decimal, char, enumeration types, and pointer types (T*). The operator exists in two distinct syntactical forms, which dictate the order of evaluation relative to the assignment or expression execution: Prefix Decrement (--x) The operand is decremented first, and the expression evaluates to the new decremented value. Postfix Decrement (x--) The expression evaluates to the original value of the operand, and the operand is decremented immediately afterward.
int prefixOperand = 10;
int prefixResult = --prefixOperand; 
// prefixOperand is mutated to 9.
// prefixResult is assigned 9.

int postfixOperand = 10;
int postfixResult = postfixOperand--; 
// postfixOperand is mutated to 9.
// postfixResult is assigned 10 (the original value).

Execution Mechanics

Under the hood, the -- operator performs a read-modify-write operation. The compiler translates the operation into the following sequence:
  1. Read the current value of the operand.
  2. Subtract 1 (or 1.0 for floating-point types) from the value.
  3. Write the mutated value back to the operand.
When the operand is a local variable or field, the compiler reads from and writes to the memory location directly. However, when the operand is a property or an indexer, the compiler does not mutate memory directly. Instead, it emits a call to the member’s get accessor to retrieve the value, performs the subtraction, and then emits a call to the set accessor to assign the decremented value.

Type-Specific Behaviors and Overflow

  • Integral Types: Decrementing the minimum representable value of an integral type depends on the arithmetic context. In a checked context, the operation throws a System.OverflowException. In an unchecked context, the value wraps around to the maximum representable value for that type.
  • Pointer Types (T*): In unsafe contexts, the -- operator performs pointer arithmetic. It decrements the memory address held by the pointer by sizeof(T) bytes, rather than strictly subtracting 1.
  • Characters and Enumerations: For char types, the operator decrements the underlying 16-bit unsigned integer (UTF-16 code unit). For enum types, it decrements the underlying integral type of the enumeration.

Thread Safety

Because the -- operator executes as a multi-step read-modify-write sequence, it is not atomic and therefore not thread-safe. If multiple threads apply the -- operator to the same variable concurrently, race conditions will occur, leading to lost updates. To perform an atomic decrement operation in a multithreaded context, you must bypass the -- operator and use the Interlocked class:
int sharedResource = 100;

// Non-atomic (Unsafe for multithreading)
sharedResource--;

// Atomic (Safe for multithreading)
System.Threading.Interlocked.Decrement(ref sharedResource);

Operator Overloading

User-defined types (class and struct) can overload the -- operator to define custom decrement behavior. When you overload the -- operator, you only define a single method. The C# compiler automatically derives both prefix and postfix semantics from this single implementation.
public struct Vector1D
{
    public int X { get; }
    
    public Vector1D(int x) => X = x;

    // The overloaded operator must be public, static, and return the containing type.
    public static Vector1D operator --(Vector1D operand)
    {
        return new Vector1D(operand.X - 1);
    }
}
When the compiler encounters vector-- or --vector, it invokes the overloaded method, manages the temporary storage of the original value for postfix operations, and assigns the returned instance back to the operand.
Master C# with Deep Grasping Methodology!Learn More