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.Ma开发者_StackOverflowkeSound();
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 the new
keyword to make this explicit.
In the second case you're re-implementing IAnimal
. Implementing an interface doesn't require the override
keyword (although it might have been nice if the language designers had required that).
To avoid re-implementing the interface, you could make MakeSound
virtual in Dog
, and then explicitly override it in FakeDog
. 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.
(Sorry for answering with a question, but you might genuinely find this experiment informative) What happens when you implement dog as follows:
public class Dog : IAnimal
{
public virtual void MakeSound()
{
Console.WriteLine("Woof");
}
//...
Note the "virtual"
You need to declare the MakeSound
method as virtual
in the Dog
class and override it in the FakeDog
class:
public class Dog : IAnimal
{
public virtual void MakeSound()
{
Console.WriteLine("Woof");
}
public virtual void Move()
{
Console.WriteLine("Moved");
}
}
public class FakeDog : Dog
{
public override void MakeSound()
{
Console.WriteLine("Bark");
}
}
精彩评论