C# 为什么重写方法只在我实现接口之后发生?

C# 为什么重写方法只在我实现接口之后发生?,c#,inheritance,interface,extending-classes,C#,Inheritance,Interface,Extending Classes,我一直在阅读《有效地使用遗留代码》一书,并且一直在玩弄通过创建伪代码覆盖单元测试中难以测试的方法的概念。我把一个我认为有效的例子放在一起,结果它的表现与我预期的不同。我想我刚刚在对继承和方法重载在C#中的工作原理的理解上发现了一个漏洞,我想知道是否有人能帮助我理解这里发生了什么 我有以下界面: public interface IAnimal { void MakeSound(); void Move(); } 然后,我创建了一个动物界面的实现,如下所示: public cla

我一直在阅读《有效地使用遗留代码》一书,并且一直在玩弄通过创建伪代码覆盖单元测试中难以测试的方法的概念。我把一个我认为有效的例子放在一起,结果它的表现与我预期的不同。我想我刚刚在对继承和方法重载在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();
IAnimal myanimal = new FakeDog();
myanimal.MakeSound();
myanimal.Move();
public class Dog : IAnimal
{

public virtual void MakeSound()
{
    Console.WriteLine("Woof");
}

//...
当我按如下方式使用该类时:

public class Dog : IAnimal
{

public void MakeSound()
{
    Console.WriteLine("Woof");
}

public void Move()
{
    Console.WriteLine("Moved");
}

}
IAnimal myanimal = new Dog();
myanimal.MakeSound();
myanimal.Move();
IAnimal myanimal = new FakeDog();
myanimal.MakeSound();
myanimal.Move();
public class Dog : IAnimal
{

public virtual void MakeSound()
{
    Console.WriteLine("Woof");
}

//...
我得到以下输出: 汪汪 移动

现在,让我们假设我需要对Dog类进行单元测试,但是其中一个方法MakeSound()需要被重写,因为出于某种原因,它使得类很难测试

我通过扩展dog类并创建MakeSound方法来创建一个伪dog

public class FakeDog : Dog
{
    public void MakeSound()
    {
         Console.WriteLine("Bark");
    }
}
当我按如下方式使用该类时:

public class Dog : IAnimal
{

public void MakeSound()
{
    Console.WriteLine("Woof");
}

public void Move()
{
    Console.WriteLine("Moved");
}

}
IAnimal myanimal = new Dog();
myanimal.MakeSound();
myanimal.Move();
IAnimal myanimal = new FakeDog();
myanimal.MakeSound();
myanimal.Move();
public class Dog : IAnimal
{

public virtual void MakeSound()
{
    Console.WriteLine("Woof");
}

//...
我得到以下输出: 汪汪 移动

我一直以为会是: 吠叫 移动

但是,如果我让FakeDog类实现动物接口并使用它:

public class FakeDog : Dog, IAnimal
{
    public void MakeSound()
    {
         Console.WriteLine("Bark");
    }
}
我得到以下输出: 吠叫 移动

我只是想理解为什么它现在会覆盖这个方法,就像我在扩展Dog类时所期望的那样。有人能让我明白这一点吗?

(很抱歉回答了一个问题,但你可能真的觉得这个实验很有用)当你实现dog时会发生什么,如下所示:

public class Dog : IAnimal
{

public void MakeSound()
{
    Console.WriteLine("Woof");
}

public void Move()
{
    Console.WriteLine("Moved");
}

}
IAnimal myanimal = new Dog();
myanimal.MakeSound();
myanimal.Move();
IAnimal myanimal = new FakeDog();
myanimal.MakeSound();
myanimal.Move();
public class Dog : IAnimal
{

public virtual void MakeSound()
{
    Console.WriteLine("Woof");
}

//...

注意“virtual”

您需要在
Dog
类中将
MakeSound
方法声明为
virtual
,并在
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");
    }
}

在第一种情况下,您创建的新方法隐藏了
IAnimal.MakeSound
的原始实现。您应该看到一条警告,建议您使用
new
关键字来明确这一点

在第二种情况下,您正在实现
IAnimal
。实现一个接口不需要使用
override
关键字(尽管如果语言设计者需要的话,这可能会更好)


为了避免重新实现接口,您可以在
Dog
中使
MakeSound
虚拟,然后在
FakeDog
中显式覆盖它。在这一点上,只涉及一个可能的解决方案,而且一切都更容易理解。我尽可能避免重新实现和方法隐藏。

当我这样做时,当我扩展Dog类时,我可以使用override关键字覆盖该方法。在我的例子中,我试图看看如果我不想使现有的类方法成为虚拟的,事情会如何进行,因为我在工作中遇到的大多数遗留代码没有将这些方法标记为虚拟的。我没有看到警告的原因是因为我在LinqPad中进行实验。谢谢你的解释。我必须记住,当我使用提取和重写方法重构时,我需要将该方法创建为虚拟方法,以便正确重写该方法。