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 in C# functions as both a unary and binary operator, responsible for numeric negation, arithmetic subtraction, enumeration subtraction, delegate unsubscription, and pointer arithmetic. Its behavior is determined by the arity (number of operands) and the compile-time types of those operands.

Unary Minus Operator

The unary - operator computes the numeric negation of its single operand, effectively evaluating to 0 - x. It is explicitly predefined for the int, long, float, double, and decimal types. When applied to smaller signed integral types (sbyte or short), the operand is implicitly converted to int, and the operation returns an int. When applied to a uint, the operand is implicitly converted to long, and the operation returns a long. Applying the unary - operator to a ulong results in a compile-time error, as no built-in implicit conversion exists to a signed type large enough to hold all possible ulong values.
int a = 5;
int b = -a; // b is -5

short c = 10;
int d = -c; // c is implicitly converted to int, returns int

uint e = 15;
long f = -e; // e is implicitly converted to long, returns long

Binary Subtraction Operator

The binary - operator computes the difference between its left-hand and right-hand operands. It is predefined for all integral, floating-point, and decimal numeric types. For integral types, the operation is subject to overflow checking. In an unchecked context (the default), an overflow results in truncation of the high-order bits. In a checked context, an overflow throws a System.OverflowException. Floating-point subtraction never throws an exception; overflow results in positive or negative infinity, and invalid operations result in NaN (Not a Number). Subtraction with the decimal type will throw a System.OverflowException if the result is outside the representable range.
int x = 10;
int y = 3;
int result = x - y; // result is 7

checked
{
    int min = int.MinValue;
    // int overflow = min - 1; // Throws System.OverflowException
}

Enumeration Subtraction

The C# language specification provides predefined binary - operators for all enum types, supporting two specific operations:
  1. Enum and Underlying Type: Subtracting a value of the enumeration’s underlying integral type from an enumeration instance yields a new instance of the enumeration type (Enum - UnderlyingType = Enum).
  2. Enum and Enum: Subtracting two instances of the same enumeration type yields the distance between them, represented as a value of the underlying integral type (Enum - Enum = UnderlyingType).
enum Level { Low = 10, Medium = 20, High = 30 }

Level stepDown = Level.Medium - 10;       // Yields Level.Low
int distance = Level.High - Level.Medium; // Yields 10

Delegate Removal

When applied to operands of delegate types, the binary - operator performs multicast delegate unsubscription by invoking System.Delegate.Remove. The operator searches the invocation list of the left-hand operand for the invocation list of the right-hand operand. If found, it removes the last contiguous occurrence of the right-hand list from the left-hand list.
  • If the left operand is null, the result is null.
  • If the right operand is null, or if its invocation list is not found within the left operand, the result is the unmodified left operand.
  • If the removal results in an empty invocation list, the result is null.
Action d1 = MethodA;
Action d2 = MethodB;
Action multicast = d1 + d2 + d1;

multicast = multicast - d1; // Removes the last occurrence of d1. Invocation list is now [MethodA, MethodB]

Pointer Arithmetic

In an unsafe context, the binary - operator supports pointer arithmetic in two forms:
  1. Pointer and Integer: Subtracting an integer offset n from a pointer T* ptr yields a new pointer of type T*. The memory address is decremented by n * sizeof(T).
  2. Pointer and Pointer: Subtracting a pointer T* ptr2 from another pointer T* ptr1 yields a long representing the number of elements of type T between the two memory addresses. The calculation is (ptr1 - ptr2) / sizeof(T).
unsafe
{
    int* ptr1 = (int*)0x1008;
    int* ptr2 = ptr1 - 1;     // Decrements by 1 * sizeof(int) (4 bytes). ptr2 is 0x1004
    
    long distance = ptr1 - ptr2; // distance is 1 (element)
}

Operator Overloading

User-defined types (struct or class) can overload both the unary and binary - operators using the operator keyword. If a type overloads the binary - operator, it must explicitly define the logic for the subtraction. Overloading the binary - operator does not implicitly overload the -= compound assignment operator; however, -= will automatically use the overloaded - operator.
public readonly struct Vector2
{
    public readonly int X;
    public readonly int Y;

    public Vector2(int x, int y) => (X, Y) = (x, y);

    // Unary overload
    public static Vector2 operator -(Vector2 v) => new Vector2(-v.X, -v.Y);

    // Binary overload
    public static Vector2 operator -(Vector2 left, Vector2 right) => 
        new Vector2(left.X - right.X, left.Y - right.Y);
}
Master C# with Deep Grasping Methodology!Learn More