I’ve been reading through the book Working Effectively with Legacy Code and I’ve been playing around with the concept of overriding difficult to test methods in unit tests via the creation of a fake. I put together an example of what I thought would work and it ended up behaving differently than I had been expecting. I think I’ve just discovered a hole in my understanding of how inheritance and method overloading works in C# and I was wondering if someone could help me understand what’s going on here.
I’ve got the following interface:
public interface IAnimal
{
void MakeSound();
void Move();
}
I then create an implementation of the animal interface as follows:
public class Dog : IAnimal
{
public void MakeSound()
{
Console.WriteLine("Woof");
}
public void Move()
{
Console.WriteLine("Moved");
}
}
When I use this class as follows:
IAnimal myanimal = new Dog();
myanimal.MakeSound();
myanimal.Move();
I get the following output:
Woof
Moved
Now, lets pretend that I’m needing to unit test the Dog class but one of the methods, MakeSound(), needs to be overridden because it is making the class difficult to test for some reason.
I create a fake dog by extending the Dog class and creating a method for MakeSound
public class FakeDog : Dog
{
public void MakeSound()
{
Console.WriteLine("Bark");
}
}
When I use this class as follows:
IAnimal myanimal = new FakeDog();
myanimal.MakeSound();
myanimal.Move();
I get the following output:
Woof
Moved
I had been expecting it to have been:
Bark
Moved
However, if I then have the FakeDog class implement the animal interface and use it:
public class FakeDog : Dog, IAnimal
{
public void MakeSound()
{
Console.WriteLine("Bark");
}
}
I get the following output:
Bark
Moved
I’m just wanting to understand the reason why this now overrides the method as I had been expecting when I had just been extended the Dog class. Can anyone set me straight on this?
In the first case you’re creating a new method which hides the original implementation of
IAnimal.MakeSound. You should have seen a warning suggesting that you use thenewkeyword to make this explicit.In the second case you’re re-implementing
IAnimal. Implementing an interface doesn’t require theoverridekeyword (although it might have been nice if the language designers had required that).To avoid re-implementing the interface, you could make
MakeSoundvirtual inDog, and then explicitly override it inFakeDog. At that point there’s only one possible resolution involved, and everything is simpler to understand. I try to avoid reimplementation and method hiding whenever possible.