14

为什么抽象方法需要 new/override 而虚拟方法不需要?

样品 1:

abstract class ShapesClass
{
    abstract public int Area(); // abstract!
}

class Square : ShapesClass
{
    int x, y;

    public int Area() // Error: missing 'override' or 'new'
    {
        return x * y;
    }
}

编译器将显示此错误: 要使当前成员覆盖该实现,请添加 override 关键字。否则添加新关键字

样本 2:

class ShapesClass
{
    virtual public int Area() { return 0; } // it is virtual now!
}

class Square : ShapesClass
{
    int x, y;

    public int Area() // no explicit 'override' or 'new' required
    {
        return x * y;
    }
}

通过默认隐藏该方法,这将编译得很好。

我完全理解技术差异。但是我想知道为什么语言是这样设计的。在“示例 2”中也有同样的限制不是更好吗?我的意思是在大多数情况下,如果您创建一个与父类中同名的方法,您通常打算覆盖它。所以我认为明确声明 Override/New 对虚拟方法也是有意义的。

这种行为是否有设计方面的原因?

更新: 第二个样本实际上会导致警告。第一个示例显示错误,因为需要子类来实现抽象方法。我没有看到 VS.. 中的警告现在对我来说非常有意义。谢谢。

4

5 回答 5

11

使用 .NET 3.5 SP1 中随附的 C# 3.0 编译器或 .NET 4.0 中随附的 C# 4.0 编译器,对于您的第一个示例,我收到以下错误:

错误 CS0534:“ConsoleApplication3.Square”未实现继承的抽象成员“ConsoleApplication3.ShapesClass.Area()”

第二个警告如下:

警告 CS0114:“ConsoleApplication3.Square.Area()”隐藏了继承的成员“ConsoleApplication3.ShapesClass.Area()”。要使当前成员覆盖该实现,请添加 override 关键字。否则添加新关键字。

在第一种情况下,这是一个错误,因为您实际上并没有覆盖基方法,这意味着具体类中没有抽象方法的实现。在第二种情况下,这是一个警告,因为代码在技术上是正确的,但编译器怀疑这不是您的意思。这是启用“将警告视为错误”编译设置通常是个好主意的原因之一。

所以我无法重现你的行为,编译器的行为对我来说是正确的。您使用的是哪个版本的编译器?

于 2010-09-03T09:47:54.413 回答
7

这是直接来自 C# 规范的答案。

...从继承的范围中隐藏可访问的名称会导致报告警告。在示例中

class Base
{
    public void F() {}
}
class Derived: Base
{
    public void F() {}      // Warning, hiding an inherited name
}

在 Derived 中声明 F 会导致报告警告。隐藏继承的名称并不是一个错误,因为这会阻止基类的单独演变。例如,上述情况的发生可能是因为 Base 的较新版本引入了该类的早期版本中不存在的 F 方法。如果上述情况是错误的,那么对单独版本化的类库中的基类所做的任何更改都可能导致派生类变得无效。通过使用 new 修饰符可以消除隐藏继承名称引起的警告:

class Base
{
    public void F() {}
}
class Derived: Base
{
    new public void F() {}
}

new 修饰符表示 Derived 中的 F 是“新的”,并且确实是为了隐藏继承的成员。

于 2010-09-03T09:56:07.063 回答
3

不同之处在于必须重写抽象方法,而虚拟方法则不需要。

在不实现所有抽象成员的情况下继承抽象类(在非抽象类中)是错误的,但是在从类继承而不指定overridenew为虚拟方法时只会收到警告。

于 2010-09-03T09:45:15.553 回答
0

我认为在第一种方式中你会得到编译器错误,因为抽象方法是隐藏的并且没有实现(所以实际上你的类是错误的,而且很难找出原因)。在第二种情况下,您只会收到警告,因为该类是可用的。

于 2010-09-03T09:46:54.880 回答
0

版本控制

乍一看,您可能会认为方法隐藏似乎不是特别有用。但是,在某些情况下您可能需要使用它。假设您想使用MotorVehicle由另一个程序员编写的名为的类,并且您想使用该类派生您自己的类。此外,我们还假设您想Accelerate()在派生类中定义一个方法。例如:

public class Car : MotorVehicle
{
    // define the Accelerate() method
    public void Accelerate()
    {
        Console.WriteLine(“In Car Accelerate() method”);
        Console.WriteLine(model + “ accelerating”);
    }
}

接下来,假设另一个程序员后来修改了 MotorVehicle 类并决定添加她自己的virtual Accelerate()方法:

public class MotorVehicle
{
    // define the Accelerate() method
    public virtual void Accelerate()
    {
        Console.WriteLine(“In MotorVehicle Accelerate() method”);
        Console.WriteLine(model + “ accelerating”);
    }
}

其他程序员添加此Accelerate()方法会导致一个问题: Accelerate()您的类中的方法隐藏了现在在他们的类中定义Car的继承方法。稍后,当您编译您的类时,编译器并不清楚您是否真的打算让您的方法隐藏继承的方法。因此,当您尝试编译 Car 类时,编译器会报告以下警告:Accelerate()MotorVehicleCar

警告 CS0114:“Car.Accelerate()”隐藏了继承的成员“MotorVehicle.Accelerate()”。要使当前成员覆盖该实现,请添加 override 关键字。否则添加新关键字。

于 2014-03-13T11:38:54.413 回答