在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#选择此接口实现的原因。
答案 0 :(得分:1)
我看过这个问题。
Interface inheritance in ComVisible classes in C#
和这个
C# exposing to COM - interface inheritance
据我所知,如果您有两个对象并希望它们通过COM可见,则两者都应该明确地继承所需的接口。
答案 1 :(得分:-1)
如果Lion : Cat
和Cat : 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。
对接口进行编程还可以让您更轻松地接受控制/依赖注入的反转。