During Episode 1, we discussed the pitfalls of using explicit interface method implementations. I’ve put this article together to illustrate some of the confusing scenarios. The intent of this article isn’t to illustrate all of the pitfalls, but instead to illustrate enough to help explain why explicit interface method implementations should be carefully considered before use.
First, let’s briefly go over the structure of these examples. Each example program creates a simple command line application that creates a list of a class type, with the intention of displaying the list contents before and after a sort operation. Since I want to focus on the interfaces, the example application classes will all inherit from a base class, Example, that provides a static method, SomeExample(Type t), that will be the heart of each command line application. This will allow us to simply pass in the class type that we want to use.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
public class Example { protected static void SomeExample(Type t) { Console.Clear(); var random = new Random(); var list = new List<SomeClass>(); for (var i = 0; i < 10; i++) { var sometype = (SomeClass) Activator.CreateInstance(t); sometype.SomeData = random.Next (); list.Add(sometype); } Console.WriteLine("Before IComparable comparisons of {0}.", t); list.ForEach(x => Console.WriteLine(x.SomeData.ToString("N0"))); Console.WriteLine("nWhich IComparable was called?..."); list.Sort(); Console.WriteLine("nAfter IComparable comparisons of {0}.", t); list.ForEach(x => Console.WriteLine(x.SomeData.ToString("N0"))); Console.WriteLine("nPress ENTER to continue..."); Console.ReadLine(); } } |
To set this scenario up, we first need to create a base class that implements an interface explicitly. Since IComparable only requires we implement one method, it makes for a nice and simple interface for use to illustrate this.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public class SomeClass : IComparable { public Int32 SomeData { get; set; } // Marked as private by the compiler. int IComparable.CompareTo(object obj) { Console.WriteLine("SomeClass IComparable.CompareTo."); if (obj == null) return 1; var sc = obj as SomeClass; if (sc != null) { return SomeData.CompareTo(sc.SomeData); } else { throw new ArgumentException("Oops! That's not SomeClass!?"); } } } |
As you can see, there’s nothing fancy here. Our base class, SomeClass, provides some storage for an integer, SomeData, and explicitly implements IComparable.CompareTo. To help see what happens when we do any comparisons on SomeClass, I’ve added a Console.WriteLine statement.
So I’ll create a simple command line application that looks like the following.
11 12 13 14 15 16 17 |
class Example1 : Example { static void Main(string[] args) { SomeExample(typeof(SomeClass)); } } |
And it produces output similar to this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
Before IComparable comparisons of BadEIMIExamples.SomeClass. 1,644,850,135 321,570,261 618,441,483 1,968,351,395 1,986,367,906 391,374,060 1,412,033,816 798,387,793 2,124,274,365 917,262,458 Which IComparable was called?... SomeClass IComparable.CompareTo. ...(removed repeated output) SomeClass IComparable.CompareTo. After IComparable comparisons of BadEIMIExamples.SomeClass. 321,570,261 391,374,060 618,441,483 798,387,793 917,262,458 1,412,033,816 1,644,850,135 1,968,351,395 1,986,367,906 2,124,274,365 Press ENTER to continue... |
Everything is as expected so far, again, nothing fancy. We have a List of SomeClass objects, each with an assigned random number. When we print out the original version of the list, we see the objects in the order they were added to the list. Next, we see some debug statements displaying which IComparable.CompareTo method was called. And lastly, we see what the list looks like after the list has been sorted, with the results listed in order of lowest to highest.
So far, everything looks fine.
Now let’s suppose we now have a need to subclass our base class with a new class, SomeChildClass.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class SomeChildClass : SomeClass, IComparable { // Well this can be confusing. // Notice that this is not declared with the override keyword public int CompareTo(object obj) { Console.WriteLine("SomeChildClass IComparable.CompareTo."); if (obj == null) return 1; var sc = obj as SomeChildClass; if (sc != null) { return SomeData.CompareTo(sc.SomeData) * -1; } else { throw new ArgumentException("Oops! That's not SomeChildClass!?"); } } } |
At first glance, there isn’t much difference. The Console.WriteLine statement writes a different message, and the result is multiplied by -1, which produces output similar to this with the results listed in order of highest to lowest.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
Before IComparable comparisons of BadEIMIExamples.SomeChildClass. 370,184,421 80,315,015 1,324,715,090 617,973,614 391,743,945 668,384,818 1,171,393,248 1,859,564,318 1,563,397,975 173,276,754 Which IComparable was called?... SomeChildClass IComparable.CompareTo. ...(removed repeated output) SomeChildClass IComparable.CompareTo. After IComparable comparisons of BadEIMIExamples.SomeChildClass. 1,859,564,318 1,563,397,975 1,324,715,090 1,171,393,248 668,384,818 617,973,614 391,743,945 370,184,421 173,276,754 80,315,015 Press ENTER to continue... |
Let the confusion start. For starters, notice that the implementation of CompareTo isn’t explicit in the subclass and it’s not declared with the override keyword. Also, notice that we declared CompareTo as public.
Let’s look at a third example where I’ve removed the public keyword.
11 12 13 14 15 16 17 18 19 20 |
public class SomeOtherChildClass : SomeClass { // Notice that this is not implementing the IComparable interface, // it is just a method with the same signature as IComparable's. int CompareTo(object obj) { Console.WriteLine("SomeOtherChildClass.CompareTo. NEVER CALLED."); return ((IComparable)this).CompareTo(obj); } } |
Now, let’s see what happens when we run this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
Before IComparable comparisons of BadEIMIExamples.SomeOtherChildClass. 1,753,308,536 687,128,889 66,852,233 1,534,124,255 1,534,968,184 980,682,314 425,075,692 1,182,473,789 1,574,743,759 1,168,328,658 Which IComparable was called?... SomeClass IComparable.CompareTo. ...(removed repeated output) SomeClass IComparable.CompareTo. After IComparable comparisons of BadEIMIExamples.SomeOtherChildClass. 66,852,233 425,075,692 687,128,889 980,682,314 1,168,328,658 1,182,473,789 1,534,124,255 1,534,968,184 1,574,743,759 1,753,308,536 Press ENTER to continue... |
Wait, what? The parent’s private explicit interface method implemenation was called, as noted by the output and the sorted list’s printed order. But why?
For starters, by not declaring SomeOtherChildClass.CompareTo as public, the compiler automatically marks it as private. Now we have an object with two private methods that seem to share the same name, but only the one from the base class is the interface implementation. Why did I say they seem to share the same name? They don’t share the same name since the method from the base class is named explicitly. See how others reading this code could easily get confused?
When the sort operation calls IComparable.CompareTo, only the method from the base class can be called since it’s the only implementation of the interface.
OK, OK, so maybe we just need to let our subclass call our base class’s implementation. But how? The method is marked private by the base class, so we can’t call base.CompareTo. Here’s a temptation.
11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class SomeFinalChildClass : SomeClass, IComparable { public int CompareTo(object obj) { // We want to call the parent, but unfortunately, this method isn't an option // because base.CompareTo is marked as private. // return base.CompareTo(obj); // be careful.... Console.WriteLine("SomeFinalChildClass call to IComparable.CompareTo creates an infinite loop!"); return ((IComparable)this).CompareTo(obj); } } |
This looks OK, right? We’ll just cast this to IComparable and call CompareTo. Let’s see what happens now.
1 2 3 4 |
...(removed repeated output) SomeFinalChildClass call to IComparable.CompareTo creates an infinite loop! Process is terminated due to StackOverflowException. |
StackOverflowException from too much recursion huh? Well that sucks. So how do we call the base class? We would have needed to create a virtual method, CompareThis, in our base class, SomeClass, and allow it to be the meat of our comparison calculation. Then in our subclass, we could use the override keyword to create a new method that can safely call the base class method when and if needed.
So, I’ve modified the base class to look like this.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
public class SomeClass : IComparable { public Int32 SomeData { get; set; } // Marked as private by the compiler. int IComparable.CompareTo(object obj) { Console.WriteLine("SomeClass IComparable.CompareTo."); return CompareThis(obj); } public virtual int CompareThis(object obj) { //return ((IComparable)this).CompareTo(obj); Console.WriteLine("SomeClass CompareThis."); if (obj == null) return 1; var sc = obj as SomeClass; if (sc != null) { return SomeData.CompareTo(sc.SomeData); } else { throw new ArgumentException("Oops! That's not SomeClass!?"); } } } |
And I’ve modified the child class to look like this.
11 12 13 14 15 16 17 18 |
public class SomeFinalChildClass : SomeClass, IComparable { public int CompareTo(object obj) { Console.WriteLine("SomeFinalChildClass calls base.CompareThis."); return base.CompareThis(obj); } } |
Which produces this final output.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
Before IComparable comparisons of BadEIMIExamples.SomeFinalChildClass. 626,958,353 2,008,153,586 368,830,372 1,446,025,894 442,346,676 1,102,225,049 1,313,472,060 2,109,993,997 12,395,207 136,566,420 Which IComparable was called?... SomeFinalChildClass calls base.CompareThis. SomeClass CompareThis. ...(removed repeated output) SomeFinalChildClass calls base.CompareThis. SomeClass CompareThis. After IComparable comparisons of BadEIMIExamples.SomeFinalChildClass. 12,395,207 136,566,420 368,830,372 442,346,676 626,958,353 1,102,225,049 1,313,472,060 1,446,025,894 2,008,153,586 2,109,993,997 Press ENTER to continue... |
Now, that works just fine, as along as you have the access and ability to modify the base class. However, that’s not always the case.
As stated earlier, if you can avoid explicit interface method implementations, then you probably should. But when you must use them, do so with caution and be aware of the pitfalls.
Download the examples discussed in this article here.