Caution when using operators for overloading and conversion

I have a class that defines several math operations and conversion between other types that was giving unexpected results. After quite a while of debugging, I found that the problem was with the way the compiler casts operands when using operators. My original class was similar to this:

public class MyNumericClassDefinedOperator
{
    protected decimal _MyDecimalValue;

    public decimal MyDecimalValue
    {
        get { return _MyDecimalValue; }
        set { _MyDecimalValue = value; }
    }

    public MyNumericClassDefinedOperator(object x)
    {
        try
        {
            _MyDecimalValue = Decimal.Parse(x.ToString());
        }
        catch { }
    }

    #region Operators with MyNumericClassDefinedOperator
    public static MyNumericClassDefinedOperator operator +(MyNumericClassDefinedOperator x, MyNumericClassDefinedOperator y)
    {
        return new MyNumericClassDefinedOperator(x._MyDecimalValue + y._MyDecimalValue);
    }
    #endregion

    #region Conversion Operators
    public static implicit operator decimal(MyNumericClassDefinedOperator x)
    {
        return x.MyDecimalValue;
    }
    public static implicit operator int(MyNumericClassDefinedOperator x)
    {
        return (int)x.MyDecimalValue;
    }
    public static implicit operator MyNumericClassDefinedOperator(decimal x)
    {
        return new MyNumericClassDefinedOperator(x);
    }
    public static implicit operator MyNumericClassDefinedOperator(int x)
    {
        return new MyNumericClassDefinedOperator(x);
    }
    #endregion

    #region Operators with int
    public static decimal operator +(MyNumericClassDefinedOperator x, int y)
    {
        return (x.MyDecimalValue + y);
    }
    public static decimal operator *(MyNumericClassDefinedOperator x, int y)
    {
        return (x.MyDecimalValue * y);
    }
    #endregion

    #region Operators with decimal
    public static decimal operator +(MyNumericClassDefinedOperator x, decimal y)
    {
        return (x.MyDecimalValue + y);
    }
    public static decimal operator *(MyNumericClassDefinedOperator x, decimal y)
    {
        return (x.MyDecimalValue * y);
    }
    #endregion
}

This simple test gives the incorrect result 54.0:

MyNumericClassDefinedOperator Value1 = 17.656m;
MyNumericClassDefinedOperator Value2 = 1.67845m;
MyNumericClassDefinedOperator Value3 = 4.23905m;
MyNumericClassDefinedOperator Value4 = 9.0564m;
MyNumericClassDefinedOperator Value5 = 3.1345m;
MyNumericClassDefinedOperator Value6 = 0.671m;

MyNumericClassDefinedOperator Result1 = 1.5m * (Value1 + Value2 + Value3 + Value4 + Value5 + Value6);
Console.WriteLine(Result1.MyDecimalValue.ToString());

When I removed the + and * operators with decimal and int (requiring me to add the operator MyNumericClass *(MyNumericClass, MyNumericClass)), the class looks like this:

public class MyNumericClass
{
    protected decimal _MyDecimalValue;

    public decimal MyDecimalValue
    {
        get { return _MyDecimalValue; }
        set { _MyDecimalValue = value; }
    }

    public MyNumericClass(object x)
    {
        try
        {
            _MyDecimalValue = Decimal.Parse(x.ToString());
        }
        catch { }
    }

    #region Operators with MyNumericClass
    public static MyNumericClass operator +(MyNumericClass x, MyNumericClass y)
    {
        return new MyNumericClass(x._MyDecimalValue + y._MyDecimalValue);
    }
    public static MyNumericClass operator *(MyNumericClass x, MyNumericClass y)
    {
        return new MyNumericClass(x._MyDecimalValue * y._MyDecimalValue);
    }
    #endregion

    #region Conversion Operators
    public static implicit operator decimal(MyNumericClass x)
    {
        return x.MyDecimalValue;
    }
    public static implicit operator int(MyNumericClass x)
    {
        return (int)x.MyDecimalValue;
    }
    public static implicit operator MyNumericClass(decimal x)
    {
        return new MyNumericClass(x);
    }
    public static implicit operator MyNumericClass(int x)
    {
        return new MyNumericClass(x);
    }
    #endregion
}

and it gives the correct result, 54.653100:

MyNumericClass Value11 = 17.656m;
MyNumericClass Value12 = 1.67845m;
MyNumericClass Value13 = 4.23905m;
MyNumericClass Value14 = 9.0564m;
MyNumericClass Value15 = 3.1345m;
MyNumericClass Value16 = 0.671m;

MyNumericClass Result2 = 1.5m * (Value11 + Value12 + Value13 + Value14 + Value15 + Value16);
Console.WriteLine(Result2.MyDecimalValue.ToString());

The rule that is implied by this is: Use your own operators to take C# classes into your type, where you control the outcome, rather than letting the compiler choose.

(Side note: I was also able to get correct results simply by adding this operator to the class:

public static MyNumericClassDefinedOperator operator *(MyNumericClassDefinedOperator x, MyNumericClassDefinedOperator y)
{
	return new MyNumericClassDefinedOperator(x._MyDecimalValue * y._MyDecimalValue);
}

without removing the + and * operators with decimal and int, and it indeed gave the right result. I don’t plan on using this method, as removing the decimal and int operators removes possible other options from the compiler. Plus, it is probably clearer to anyone else reviewing the code and it’s easier to code because there’s only one place where the operations are done.)

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s