C设计:C#design:为什么抽象方法需要new / override,虚拟方法不需要?

C# design: Why is new/override required on abstract methods but not on virtual methods?

为什么抽象方法需要新的/重写,而虚拟方法不需要?

样品1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.》中看到警告。现在对我来说非常有意义。谢谢。


使用.NET 3.5 SP1中附带的C 3.0编译器或.NET 4.0中附带的C 4.0编译器,第一个示例中出现以下错误:

error CS0534: 'ConsoleApplication3.Square' does not implement inherited abstract member 'ConsoleApplication3.ShapesClass.Area()'

第二个警告如下:

warning CS0114: 'ConsoleApplication3.Square.Area()' hides inherited member 'ConsoleApplication3.ShapesClass.Area()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.

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

所以我不能责备你的行为,编译器的行为对我来说是正确的。您使用的编译器是哪个版本?


这是C规格的直接答案。

...hiding an accessible name from an
inherited scope causes a warning to be
reported. In the example

1
2
3
4
5
6
7
8
class Base
{
    public void F() {}
}
class Derived: Base
{
    public void F() {}      // Warning, hiding an inherited name
}

the declaration of F in Derived causes
a warning to be reported. Hiding an
inherited name is specifically not an
error, since that would preclude
separate evolution of base classes.
For example, the above situation might
have come about because a later
version of Base introduced an F method
that wasn’t present in an earlier
version of the class. Had the above
situation been an error, then any
change made to a base class in a
separately versioned class library
could potentially cause derived
classes to become invalid. The warning
caused by hiding an inherited name can
be eliminated through use of the new
modifier:

1
2
3
4
5
6
7
8
class Base
{
    public void F() {}
}
class Derived: Base
{
    new public void F() {}
}

The new modifier indicates that the F
in Derived is"new", and that it is
indeed intended to hide the inherited
member.


区别在于抽象方法必须被重写,而虚拟方法不能。

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


版本控制

乍一看,你可能认为隐藏方法并没有特别有用。有一种情况但是,您可能需要在哪里使用它。假设您想使用一个名为MotorVehicle的类,是由另一个程序员编写的,您希望使用这个类来派生自己的类。此外,我们还假设您希望在派生类中定义一个Accelerate()方法。例如:

1
2
3
4
5
6
7
8
9
public class Car : MotorVehicle
{
    // define the Accelerate() method
    public void Accelerate()
    {
        Console.WriteLine("In Car Accelerate() method");
        Console.WriteLine(model +" accelerating");
    }
}

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

1
2
3
4
5
6
7
8
9
public class MotorVehicle
{
    // define the Accelerate() method
    public virtual void Accelerate()
    {
        Console.WriteLine("In MotorVehicle Accelerate() method");
        Console.WriteLine(model +" accelerating");
    }
}

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

warning CS0114: ‘Car.Accelerate()’ hides inherited member
‘MotorVehicle.Accelerate()’. To make the current member override that
implementation, add the override keyword. Otherwise add the new keyword.


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