在基类和派生类中实现接口

时间:2014-06-27 17:11:15

标签: c# interface

在C#中,您能举出一个很好的例子,说明为什么要在基类上实现接口并在派生类上重新实现该接口,而不是将基类方法设置为虚拟。

例如:

interface IMakesNoise
{
  void Speak();
}

class Cat : IMakesNoise
{
  public void Speak()
  {
    Console.WriteLine("MEOW");
  }
}

class Lion : Cat, IMakesNoise
{
  public new void Speak()
  {
    Console.WriteLine("ROAR");
  }
}

测试行为:

Cat cat = new Cat();
Cat lion = new Lion();

// Non virtual calls, acts as expected    
cat.Speak();
lion.Speak();

// Grabbing the interface out is 'virtual' in that it grabs the most derived interface implementation
(cat as IMakesNoise).Speak();
(lion as IMakesNoise).Speak();

这将打印出来:

MEOW
MEOW
MEOW
ROAR

更新:有关 why 的更多说明,原因是我正在实现编译器,我想知道C#选择此接口实现的原因。

3 个答案:

答案 0 :(得分:1)

我看过这个问题。

Interface inheritance in ComVisible classes in C#

和这个

C# exposing to COM - interface inheritance

据我所知,如果您有两个对象并希望它们通过COM可见,则两者都应该明确地继承所需的接口。

答案 1 :(得分:-1)

如果Lion : CatCat : IMakeNoise然后通过及物性,Lion : IMakeNoise自动 - 在Lion上声明它是多余且不必要的。这是因为Lion不能继承Cat所有Cat属性 - 包括接口。

存在虚方法,不仅可以覆盖,还可以完全替换派生层次结构中的功能。换句话说,您可以在更加派生的类中更改较少派生类的功能。这与阴影不同,阴影仅覆盖给定派生类的方法功能,而不是整个类层次结构。

通过声明基类中存在的相同非虚方法来完成阴影,同时添加new关键字以指示阴影行为是预期的。

覆盖是通过声明基类中存在的相同virtual方法完成的,同时添加override关键字以指示覆盖行为是有意的。

代码示例将使这些差异非常明确。让我们定义一个基类Vehicle类(作为抽象,以便它不能被实例化)和一个派生Motorcycle类。两者都会向控制台输出有关它们具有的车轮数量的信息:

/// <summary>
/// Represents a Vehicle.
/// </summary>
public abstract class Vehicle
{
    /// <summary>
    /// Prints the Number of Wheels to the Console.
    /// Virtual so can be changed by more derived types.
    /// </summary>
    public virtual void VirtualPrintNumberOfWheels()
    {
        Console.WriteLine("Number of Wheels: 4");
    }

    /// <summary>
    /// Prints the Number of Wheels to the Console.
    /// </summary>
    public void ShadowPrintNumberOfWheels()
    {
        Console.WriteLine("Number of Wheels: 4");
    }
}

/// <summary>
/// Represents a Motorcycle.
/// </summary>
public class Motorcycle : Vehicle
{
    /// <summary>
    /// Prints the Number of Wheels to the Console.
    /// Overrides base method.
    /// </summary>
    public override void VirtualPrintNumberOfWheels()
    {
        Console.WriteLine("Number of Wheels: 2");
    }

    /// <summary>
    /// Prints the Number of Wheels to the Console.
    /// Shadows base method.
    /// </summary>
    public new void ShadowPrintNumberOfWheels()
    {
        Console.WriteLine("Number of Wheels: 2");
    }
}

上面我们定义了两个类:一个抽象基类Vehicle类,它有一个虚拟和非虚方法,它们都做同样的事情,还有一个Motorcycle类实现了{{1}在重写虚方法并遮蔽普通方法时类。现在我们将调用具有不同类型签名的方法来查看差异:

Vehicle

结果:

static void Main(string[] args)
{
    // Instantiate a Motorcycle as type Motorcycle
    Motorcycle vehicle = new Motorcycle();

    vehicle.ShadowPrintNumberOfWheels();
    vehicle.VirtualPrintNumberOfWheels();

    // Instantiate a Motorcycle as type Vehicle
    Vehicle otherVehicle = new Motorcycle();

    // Calling Shadow on Motorcycle as Type Vehicle
    otherVehicle.ShadowPrintNumberOfWheels();
    otherVehicle.VirtualPrintNumberOfWheels();

    Console.ReadKey();
}

答案 2 :(得分:-1)

我在上面的代码示例中遇到了一些问题,但是我将大部分内容放在一边并专注于最重要的问题:它违反了编程到接口的原则,而不是实现。如果您将cat和lion实例的声明更改为以下内容,则您遇到的问题就会消失。

IMakesNoise cat = new Cat();
IMakesNoise lion = new Lion();

在这种情况下,您的输出是预期的MEOW,ROAR,MEOW,ROAR。请参阅小提琴here

对接口进行编程还可以让您更轻松地接受控制/依赖注入的反转。