As I mentioned in my last post about the Typesafe Enum Pattern in C#/VB.NET, I left out a few features that I had originally wanted to include in that post.
Namely, these features were nothing more than implementing the IComparable<T> and IEquatable<T> interfaces.
Upon further investigation, I’ve found that there is no need to implement IEquatable<T> here, as we’ll see in an example in just a moment.
Also, I’m not sure how often we’d find ourselves needing to sort collections of our enum-esque class.
Here’s the original class from the previous post:
The ‘Enum’ Class
This will be our baseline to begin implementing the interface(s):
public class UselessItem
{
public string ItemName { get; private set; }
public Guid ItemId { get; private set; }
private UselessItem() { }
private UselessItem(string itemName, Guid itemId)
{
ItemName = itemName;
ItemId = itemId;
}
// Define the 'Enum' members
public static UselessItem MacBook =
new UselessItem("MacBook/MacBook Pro", new GUID("EAD0014A-3733-DD11-9DCA-0019B9B42749"));
public static UselessItem ScreenDoorOnASubmarine =
new UselessItem("Screen Door on a Submarine", new GUID("EAD0014A-3733-DD11-9DCA-0019B9852F2E"));
public static UselessItem DehydratedWater =
new UselessItem("Dehydrated Water", new GUID("EAD0014A-3733-DD11-9DCA-0019B2635342"));
}
Again, if you’d like to re-familiarize yourself with the above code and its features, check out the original post: Typesafe Enum Pattern in C#/VB.NET.
Do We Need IEquatable<T>?
See for yourself:
var ui1 = UselessItem.MacBook; var ui2 = UselessItem.DehydratedWater; var r1 = (ui1 == UselessItem.MacBook); // r1 = true var r2 = (ui1 == ui2); // r2 = false var r3 = (ui2 == UselessItem.MacBook); // r3 = false
This behaves just as a normal Enum would. Clearly, there is no need to implement IEquatable<T> in this case.
Do We Need IComparable<T>
IComparable<T>, on the other hand, might prove useful if we ever find ourselves wanting to sort an Array, etc, of UselessItem pseudo-Enums.
Without implementing IComparable<T>, we are not able to do:
var uis = new[]
{
UselessItem.MacBook,
UselessItem.DehydratedWater,
UselessItem.ScreenDoorOnASubmarine,
UselessItem.MacBook
};
var s = string.Empty;
foreach (var ui in uis)
{
s += ui.ItemName + " ";
}
// s = MacBook DehydratedWater ScreenDoorOnASubmarine MacBook
// And this won't work (yet):
Array.Sort(uis, (ui1, ui2) => ui1.CompareTo(ui2)); // CompareTo not defined for type UselessItem!
Implementing IComparable<T>
IComparable<T> is definitely one of the easier interfaces to implement – it only requires the implementation of a single method:
int CompareTo(T other);
CompareTo returns:
If this object is equal to the other argument, the method returns zero.
If this object is greater than the other argument, the method returns a positive integer.
See MSDN’s Entry on IComparable<T> for more information.
This mechanism is what allows, for example, Array.Sort() to work easily with a simple lambda expression.
So let’s implement it!
// The 'enum' members have been omitted for brevity
public class UselessItem : IComparable<UselessItem>
{
public string ItemName { get; private set; }
public Guid ItemId { get; private set; }
private UselessItem() { }
private UselessItem(string itemName, Guid itemId)
{
ItemName = itemName;
ItemId = itemId;
}
// Implementing interface method here
public int CompareTo(UselessItem other)
{
return this.ItemName.CompareTo(other.ItemName);
}
// ... snip ...
In line 18, you can see that we want to be able to sort by our ItemName property. Sorting the GUID ItemIDs is probably something of little interest, but it certainly can be done as well. In fact, this pattern can be used to make our enum-esque Class sortable by whatever property/datatype we might need for our purposes.
Let’s try a sample sort:
var uis = new[]
{
UselessItem.MacBook,
UselessItem.DehydratedWater,
UselessItem.ScreenDoorOnASubmarine,
UselessItem.MacBook
};
var s = string.Empty;
foreach (var ui in uis)
{
s += ui.ItemName + " ";
}
// s = MacBook DehydratedWater ScreenDoorOnASubmarine MacBook
// Now this works!
Array.Sort(uis, (ui1, ui2) => ui1.CompareTo(ui2));
s = string.Empty;
foreach (var ui in uis)
{
s += ui.ItemName + " ";
}
// s = DehydratedWater MacBook MacBook ScreenDoorOnASubmarine
// Sort in the other direction
Array.Sort(uis, (ui1, ui2) => ui2.CompareTo(ui1));
var s2 = string.Empty;
foreach (var ui in uis)
{
s2 += ui.ItemName + " ";
}
// s2 = "ScreenDoorOnASubmarine MacBook MacBook DehydratedWater"
Not too shabby!
Conclusion
Well, we haven’t added a ton of real-world useful functionality to our enum-esque Class, though it was an easy way to demonstrate the implementation and usefulness of IComparable<T>. Initially, I had thought that implementing IEquatable<T> was also necessary to do simple operations such as:
var ui1 = UselessItem.DehydratedWater;
if (ui1 == UselessItem.MacBook)
{
Console.WriteLine("Sorry you ended up w/a MacBook! Dehydrated Water is slightly more useful!");
}
else
{
Console.WriteLine("Your item might be worthless, but at least it's not a MacBook!");
}
But, as it turns out, the nature of our enum-esque class gives us the above capability out-of-the-box.
And free stuff, that works as expected? A rarity these days!









