In a previous article, I wrote how to parse Roman Numerals using C#. This article will focus on how to calculate with the class intuitively. It will show how to implement implicit casting and the add and subtraction operator overloads. Fun stuff that's probably useful in other projects.
It would be great to do things like this:
RomanNumeral X = "X";
string result = X + "IV" + 8;
Implement implicit casting
The first thing we must implement is implicit casting. This will enable the class to interact with strings and integers without having to cast them to a RomanNumeral
first. Let's look at the code:
public static implicit operator int(RomanNumeral r)
{
return (r?.Number).GetValueOrDefault();
}
public static implicit operator RomanNumeral(int r)
{
return new RomanNumeral(r);
}
public static implicit operator string(RomanNumeral r)
{
return r?.ToString();
}
public static implicit operator RomanNumeral(string r)
{
return Parse(r);
}
What are the drawbacks of this method? Well, the Parse
might return a null
when the string is not parsable (l33t is not a valid Roman Numeral). This might result in a null-reference exception. The constructor method will throw an exception when anything below 0 is passed. This is something you might want to work around.
Plus and minus operator overloading
Now let's implement the arithmetical operators +
, -
, /
, *
and %
for the RomanNumeral
class. Notice that negative numbers are not supported in this example. We'll default to 0 (or nulla as the Romans would call it).
public static RomanNumeral operator +(RomanNumeral r1, RomanNumeral r2)
{
return r1?.Number + r2?.Number;
}
public static RomanNumeral operator -(RomanNumeral r1, RomanNumeral r2)
{
var n = r1?.Number - r2?.Number;
if (n < 0)
{
n = 0;
}
return new RomanNumeral(n.GetValueOrDefault());
}
public static RomanNumeral operator /(RomanNumeral r1, RomanNumeral r2)
{
return r1?.Number / r2?.Number;
}
public static RomanNumeral operator *(RomanNumeral r1, RomanNumeral r2)
{
return r1?.Number * r2?.Number;
}
public static RomanNumeral operator %(RomanNumeral r1, RomanNumeral r2)
{
return r1?.Number % r2?.Number;
}
Because we've implemented implicit conversions first the class handles operation on strings and integers automagically:
RomanNumeral I = "I";
RomanNumeral IV = "IV";
int a = IV - 1;
int b = 4 - I;
int c = IV - "I";
int d = "IV" - I;
int e = IV - I;
Assert.AreEqual(3, a);
Assert.AreEqual(3, b);
Assert.AreEqual(3, c);
Assert.AreEqual(3, d);
Assert.AreEqual(3, e);
string f = IV - 1;
string g = 4 - I;
string h = IV - "I";
string i = "IV" - I;
string j = IV - I;
Assert.AreEqual("III", f);
Assert.AreEqual("III", g);
Assert.AreEqual("III", h);
Assert.AreEqual("III", i);
Assert.AreEqual("III", j);
RomanNumeral k = IV - 1;
RomanNumeral l = 4 - I;
RomanNumeral m = IV - "I";
RomanNumeral n = "IV" - I;
RomanNumeral o = IV - I;
Assert.AreEqual("III", k.ToString());
Assert.AreEqual("III", l.ToString());
Assert.AreEqual("III", m.ToString());
Assert.AreEqual("III", n.ToString());
Assert.AreEqual("III", o.ToString());
Beautiful, I'd say!
Comparison operators
Mathematical operations are one thing, but how about comparing Roman Numerals? Implementing an IComparable
and IComparable<RomanNumeral>
will allow sorting mechanisms to handle the class.
public int CompareTo(RomanNumeral other)
{
if (object.ReferenceEquals(other, null))
{
return 1;
}
return Number.CompareTo(other.Number);
}
public int CompareTo(object obj)
{
return CompareTo(obj as RomanNumeral);
}
protected static int Compare(RomanNumeral r1, RomanNumeral r2)
{
if (object.ReferenceEquals(r1, r2))
{
return 0;
}
if (object.ReferenceEquals(r1, null))
{
return -1;
}
return r1.CompareTo(r2);
}
Notice how I'm not using the == or the != operators. This will prevent infinite loops when we implement these operators using the compare methods. The object.ReferenceEquals
will do the job.
Implementing the operator overloads
Now let's implement the ==
, !=
, <
, <=
, >
, >=
operators using the statis Compare
method.
public static bool operator ==(RomanNumeral r1, RomanNumeral r2)
{
return Compare(r1, r2) == 0;
}
public static bool operator !=(RomanNumeral r1, RomanNumeral r2)
{
return Compare(r1, r2) != 0;
}
public static bool operator <(RomanNumeral r1, RomanNumeral r2)
{
return (Compare(r1, r2) < 0);
}
public static bool operator >(RomanNumeral r1, RomanNumeral r2)
{
return (Compare(r1, r2) > 0);
}
public static bool operator <=(RomanNumeral r1, RomanNumeral r2)
{
return (Compare(r1, r2) <= 0);
}
public static bool operator >=(RomanNumeral r1, RomanNumeral r2)
{
return (Compare(r1, r2) >= 0);
}
Implement equals
The equality method can also be implemented using the CompareTo
method. Remember that the GetHashCode
should be overridden as well.
public override bool Equals(object obj)
{
return CompareTo(obj) == 0;
}
public override int GetHashCode()
{
return this.Number.GetHashCode();
}
Summary
I’ve shown how one can do arithmetic using Roman Numerals. Implicit casting and operator overloading are very helpful in this case. You can download the class from GitHub and use it in your projects.