Introduction


Operator Overloading is a type of Static Polymorphism that allows you to redefine how standard C# operators (like +, -, *, ==) work with your custom classes.

Why it is “polymorphism” at all? Because the same symbol changes its behavior (its form) based on the data it touches, it is Polymorphic. , 5+5 takes Integer form, "hello" + "World" it tasks string form.

Why does it Not need inheritance? The computer doesn’t look at the “Family Tree” to use an operator. It looks at the Data Type (the Label). If the compiler sees two objects labeled Wallet, it looks inside the Wallet class for the + rule. It doesn’t care if Wallet has a father, a grandfather, or is an orphan.

Why is it static ? In Static (Overloading): The Compiler decides while you are Building the app. It sees your code w1 + w2 and says, “I know exactly what these are. I will link this to the Wallet addition code right now.”

Normally, + only knows how to handle built-in types like int, float, and string. With Operator Overloading, you can teach the + sign how to handle your own objects, like a Vector, a Money class, or a Score system.

*Operator overloading

without overloading Imagine we are building a game and we have a class called Box. We want to add the contents of two boxes together. Without overloading, we have to create a manual method.

Whey do we need it : Clean Code. The goal of a programmer is to write code that looks like a human sentence or a math equation. Scenario: Combining two “Inventory” bags in a game.

Manual methods Without overloading, the code is wordy. It’s hard to tell at a glance what is happening.

Inventory combinedBag = bag1.MergeWith(bag2).AddBonus(extraItems);

Operator Overloading By overloading the + operator, the code becomes “Natural.” It looks like math.

Inventory combinedBag = bag1 + bag2 + extraItems;

It reduces “Mental Friction.” The developer doesn’t have to remember if you named the method Add(), Merge(), or Combine(). They just use the universal symbol: +

class Box
{
    public int items;
    public Box(int count) { items = count; }

    // Manual Method: The "Ugly Way"
    public Box Combine(Box other)
    {
        return new Box(this.items + other.items);
    }
}

// Execution:
Box b1 = new Box(10);
Box b2 = new Box(20);

// This works, but it's "ugly" and wordy
Box b3 = b1.Combine(b2);
Console.WriteLine ( b3.items);

The Static Polymorphism way Instead of a word like Combine, we teach the + symbol how to handle the Box type.

class Box
{
    public int items;
    public Box(int count) { items = count; }

    // THE MAGIC LINE: 
    // It must be 'public static' and use the 'operator' keyword
    public static Box operator +(Box b1, Box b2)
    {
        return new Box(b1.items + b2.items);
    }
}

// Execution:
Box b1 = new Box(10);
Box b2 = new Box(20);

// Now we can do real math! 
Box b3 = b1 + b2;
Console.WriteLine ( b3.items);

The Rules

  • Public Static: Operators belong to the Class, not the Object. They must be public static.

    • When you use an operator like +, you are dealing with two different objects. If the method was not static, the computer wouldn’t know which object “owns” the addition.

    • Since you will be adding Wallets together inside your Main() method or other classes, the “plus sign” logic needs to be visible to the whole program. If it were private, only the code inside the Wallet class could use the + symbol!

  • New Objects Only: Never change the original boxes (b1 or b2). Always return a new object as the result.

  • Logical Sense: Only use + if it feels like “addition.” Don’t use + to mean “delete” or “save” that will confuse other developers!

Example

What is Vector?

In the real world, if you want to tell someone where you are, you don’t just give one number. You give coordinates. A Vector is simply a container that holds those coordinates together as a single “package.”

Think of a Vector as a physical box with two labels on it: X and Y.

  • X = How far to move Left or Right.
  • Y = How far to move Up or Down.

Instead of carrying two separate papers (one for X and one for Y), you carry one box (the Vector). This makes your “mental inventory” much cleaner.

“A Vector is a single instruction made of two parts ($X$ and $Y$). We use them so we don’t get lost in hundreds of separate variables. By using Operator Overloading, we can add these ‘instructions’ together using a simple + sign, making our code look like simple math instead of complex logic.

public struct Vector2D
{
    public float X;
    public float Y;

    public Vector2D(float x, float y)
    {
        X = x;
        Y = y;
    }
    

    // Instead of '+', we make a method called 'Add'
    public Vector2D Add(Vector2D other)
    {
        return new Vector2D(this.X + other.X, this.Y + other.Y);
    }

    // Instead of '*', we make a method called 'Multiply'
    public Vector2D Multiply(float scalar)
    {
        return new Vector2D(this.X * scalar, this.Y * scalar);
    }


    // 1. Addition: Vector + Vector
    public static Vector2D operator +(Vector2D v1, Vector2D v2)
    {
        return new Vector2D(v1.X + v2.X, v1.Y + v2.Y);
    }

    // 2. Subtraction: Vector - Vector
    public static Vector2D operator -(Vector2D v1, Vector2D v2)
    {
        return new Vector2D(v1.X - v2.X, v1.Y - v2.Y);
    }

    // 3. Multiplication (Scaling): Vector * Float
    // This allows: myVector * 5f;
    public static Vector2D operator *(Vector2D v1, float scalar)
    {
        return new Vector2D(v1.X * scalar, v1.Y * scalar);
    }

    // 4. Comparison: Vector == Vector
    // Rule: If you overload ==, you MUST overload !=
    public static bool operator ==(Vector2D v1, Vector2D v2)
    {
        return v1.X == v2.X && v1.Y == v2.Y;
    }

    public static bool operator !=(Vector2D v1, Vector2D v2)
    {
        return !(v1 == v2);
    }

    // 5. Unary Operator: -Vector (Invert)
    // This allows: v2 = -v1;
    public static Vector2D operator -(Vector2D v1)
    {
        return new Vector2D(-v1.X, -v1.Y);
    }
}
Vector2D position = new Vector2D(10, 10);
Vector2D velocity = new Vector2D(5, 0);
float deltaTime = 0.016f;

// Hard to read!
Vector2D newPos = position.Add(velocity.Multiply(deltaTime));
Vector2D position = new Vector2D(10, 10);
Vector2D velocity = new Vector2D(5, 0);
float deltaTime = 0.016f;

// Looks like real Physics!
Vector2D newPos = position + velocity * deltaTime;