我一直在阅读“有效使用遗留代码”这本书,我一直在讨论通过创建假冒在单元测试中覆盖难以测试的方法的概念。我把一个我认为会起作用的例子放在一起,最终表现得和我想象的不同。我想我刚刚发现了一个关于继承和方法重载如何在C#中工作的漏洞,我想知道是否有人可以帮助我理解这里发生了什么。
我有以下界面:
public interface IAnimal
{
void MakeSound();
void Move();
}
然后我按如下方式创建动物界面的实现:
public class Dog : IAnimal
{
public void MakeSound()
{
Console.WriteLine("Woof");
}
public void Move()
{
Console.WriteLine("Moved");
}
}
当我按如下方式使用这个类时:
IAnimal myanimal = new Dog();
myanimal.MakeSound();
myanimal.Move();
我得到以下输出: 纬 移动
现在,让我假装我需要对Dog类进行单元测试,但是其中一个方法MakeSound()需要被覆盖,因为它因为某些原因而难以测试类。
我通过扩展Dog类并为MakeSound创建方法来创建假狗
public class FakeDog : Dog
{
public void MakeSound()
{
Console.WriteLine("Bark");
}
}
当我按如下方式使用这个类时:
IAnimal myanimal = new FakeDog();
myanimal.MakeSound();
myanimal.Move();
我得到以下输出: 纬 移动
我一直期待它曾经: 吠 移动
但是,如果我让FakeDog类实现动物界面并使用它:
public class FakeDog : Dog, IAnimal
{
public void MakeSound()
{
Console.WriteLine("Bark");
}
}
我得到以下输出: 吠 移动
我只是想了解为什么现在覆盖这个方法的原因正如我刚刚延长Dog类时所期望的那样。谁能让我直截了当呢?
答案 0 :(得分:4)
在第一种情况下,您正在创建一个新方法,隐藏 IAnimal.MakeSound
的原始实现。您应该看到一条警告,建议您使用new
关键字明确说明这一点。
在第二种情况下,您重新实施 IAnimal
。实现一个接口不需要override
关键字(尽管如果语言设计者 需要的话,它可能会很好)。
为避免重新实现界面,您可以在MakeSound
中使Dog
虚拟,然后在FakeDog
中明确覆盖它。那时只涉及一种可能的解决方案,而且一切都更容易理解。我尽可能避免重新实现和方法隐藏。
答案 1 :(得分:2)
(很抱歉回答问题,但您可能真的觉得这个实验内容丰富了)当您按照以下方式实施狗时会发生什么:
public class Dog : IAnimal
{
public virtual void MakeSound()
{
Console.WriteLine("Woof");
}
//...
注意“虚拟”
答案 2 :(得分:1)
您需要在MakeSound
类中将virtual
方法声明为Dog
,并在FakeDog
类中覆盖:
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");
}
}