Simulating covariance in interface implementations
Brad Abrams always has good things to say on object model design. His recent post, Design Guidelines Update: Implementing Interfaces Privately, has caused something of an “aha” moment for me.
Consider a situation where you have a strong collection, like this:
class MyStringCollection
{
}
Ideally, you want to implement IList on this collection because, well, you can plug it into things like data binding and so on. However, IList defines its Add method as taking a System.Object as a parameter. We want to take a string. Now, we can assert inside of our Add method to ensure that we have a string, but this is less than ideal. When a developer is coding against the class using Intellisense, it will appear that the class can take any object. This is weak design - in this particular case our caller may assume that we'll ToString() the object that we pass in. Anyway, we know that working with strong collections is a good thing and we want to be as explict and unambiguous as possible when defining exactly how our classes should be used.
Apparently (and I'll put my hand up to not knowing this), you can implement the interface members privately, which means that they are not exposed through the class. It looks like this:
public void Add(string theString)
{
// add it to the list...
}
void IList.Add(object theStringAsAnObject) // note the lack of “public“ or other modifiers, and notice the reference to IList.
{
// assert...
if(!(theStringAsAnObject is string))
throw new InvalidOperationException(“It's not a string!“);
// defer to the strong method...
this.Add((string)theStringAsAnObject);
}
Any caller of the class, if they have declared that the variable they are holding the reference to the object in is of type MyStingCollection will see the first method, i.e. the one that defines a string explicitly. We've achieved the goal here that we have a strong, unambiguous class design. However, if you cast the reference to a variable of type IList, you gain access to the second, “weak” method. If we pass the collection to some UI component that supports data binding, that will use the IList interface directly and be able to call our private, weak methods.
The upshot of this design is that you can code up your objects to support a weaker interface, but give the caller a nice strong interface to call against. Ideal!