Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Operators and inheritance

My brain has turned to jelly, or I'm having an out of mind experience, or something. I'm tinkering with a class hierarchy that looks a bit like this:
enter image description here

My Money class looks like this:

public abstract class Money
{
    public int Amount { get; set; }

    public static bool operator ==(Money leftSide, Money rightSide)
    {
        // Money can only be equal if it is in the same currency.
        if (leftSide.GetType() != rightSide.GetType()) return false;
        return leftSide.Amount == rightSide.Amount;
    }

    public static bool operator !=(Money leftSide, Money rightSide)
    {
        // If the currencies are different, the amounts are always considered unequal.
        if (leftSide.GetType() != rightSide.GetType()) return true;
        return leftSide.Amount != rightSide.Amount;
    }

    public static Money operator *(Money multiplicand, int multiplier)
    {
        var result = multiplicand * multiplier;
        return result;
    }

    public static Dollar Dollar(int amount)
    {
        return new Dollar(amount);
    }

    public static Franc Franc(int amount)
    {
        return new Franc(amount);
    }
}

My Dollar operator * looks like this:

public static Dollar operator *(Dollar multiplicand, int multiplier)
{
    var result = multiplicand.Amount * multiplier;
    return new Dollar(result);
}

Now, if I run this test code, I get a Stack overflow (wahoo!)

{
    Money fiveDollars = Money.Dollar(5);
    Money timesTwo = fiveDollars*2;
}

I had expected that this would recursively call the subclass (Dollar) operator *, which would return a definite result since (Dollar * int) is defined non-recursively. Since this doesn't work, the alternative is that I have done something dumb. Why doesn't this work? What would be the right way to get this behaviour?

like image 291
Tim Long Avatar asked Nov 13 '11 02:11

Tim Long


2 Answers

You seem to have left out .Amount

public static Money operator *(Money multiplicand, int multiplier)
{
    var result = multiplicand.Amount * multiplier;
    return result;
}
like image 167
Pubby Avatar answered Oct 11 '22 13:10

Pubby


The problem is that you expect that you can override operators in derived classes and expect dynamic binding. This is not the way it works in C#. Operators are overloaded and the actual overload is chosen compile-time. This means that the following code is recursive and calls itself:

public static Money operator *(Money multiplicand, int multiplier)
{
    var result = multiplicand * multiplier;
    return result;
}

Another example where you can see the difference between operator overloading and method overriding is this:

int a = 5;
int b = 5;

Console.WriteLine(a == b); // true
Console.WriteLine(a.Equals(b)); // true
Console.WriteLine((object)a == (object)b); // false
Console.WriteLine(((object)a).Equals((object)b)); // true

In the third case, C# treats a and b as objects instead of integers, so it uses the default == operator that is used for objects: comparing references (in this case the references of boxed integers).

This can make it awkward to define operators on a class hierarchy where you want to redefine the operators in derived classes. It is especially awkward when the behavior depends on the combination of both operands, since C# (and most other OOP languages) lacks support for multiple dispatch. You can solve this by using the visitor pattern, but I think in this case you should reconsider if using subclasses for each currency is the best solution.

like image 43
Elian Ebbing Avatar answered Oct 11 '22 14:10

Elian Ebbing