C# 14: Performance-Friendly Increment Operators!

From time to time, custom classes with ++ and -- operators are necessary. The more or less big problem with this has been that it's somewhat... well, wasteful. But C# 14 finally brings an elegant solution!

The Old Problem

Let's imagine we have a Counter class and want to be able to increment it - basically the Hello World of counters:

public class Counter
{
    private int value;
    
    public static Counter operator ++(Counter c)
    {
        return new Counter(c.value + 1); // Oops, yet another new instance...
    }
}

Every time we write ++myCounter, a completely new instance is created. For a few calls - no problem. But if we do this in a hot loop, garbage accumulates pretty quickly.

The New Solution: Instance Operators

C# 14 lets you define the operators as instance methods:

public class Counter
{
    private int value;
    
    // Ta-da! No 'static', no new object
    public void operator ++()
    {
        value++; // Simply change the existing value
    }
    
    public void operator --()
    {
        value--;
    }
}

What's Different?

The new instance operators have a few clear rules:

  • public - must be public (logical)
  • No static - they are instance methods after all
  • void return type - return nothing, only modify the instance
  • No parameters - not even ones with default values

Why Is This So Cool?

In variation of Steve Ballmer: "Performance, Performance, Performance!"

Before:

for (int i = 0; i < 1000000; i++)
{
    ++myCounter; // 1 million new Counter objects...
}

Now:

for (int i = 0; i < 1000000; i++)
{
    ++myCounter; // Always the same object, only the value changes
}

Especially for value-like types such as coordinates, counters, or mathematical objects, this is a game-changer!

A Practical Example

Here's a Point type that can finally be incremented efficiently:

public class Point
{
    public int X { get; private set; }
    public int Y { get; private set; }
    
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
    
    public void operator ++()
    {
        X++;
        Y++;
    }
    
    public void operator --()
    {
        X--;
        Y--;
    }
}

// Usage:
var point = new Point(1, 1);
++point; // point is now (2, 2)
++point; // point is now (3, 3)

Thread-Safety Warning ⚠️

Important note: Instance operators modify the state of the object! This means we need to be careful with multi-threading:

// Potential race condition:
Parallel.For(0, 1000, i => ++sharedCounter);

// Better with locking:
public void operator ++()
{
    lock (lockObject)
    {
        value++;
    }
}

Happy Coding! 💻