5

我对方法覆盖和 OOP 原则的有效性有点困惑。我知道关于密封、阴影、覆盖、虚拟等的一切,但我遇到了一个场景,这让我很困惑。假设我有:

class classA
{

    public virtual void sayhello()

      {
        Console.WriteLine("hello I'm A");
    }

};

class classB :classA
{
    public override void sayhello()
    {
        Console.WriteLine("hello I'm B");
    }

};
class Program
{
    static void Main(string[] args)
    {

        classB a = new classB();
        a.sayhello();
    }
}

根据我到目前为止研究的所有内容,可以在子类中使用override关键字覆盖声明为虚拟抽象(在抽象类中)的方法。据此,上面的代码完美无缺。当我删除virtual关键字,然后尝试使用override关键字覆盖该方法时,编译器会给出错误:

无法覆盖继承的成员“inheritence.classA.sayhello()”,因为它未标记为虚拟、抽象或覆盖

然后我从子类中删除了覆盖关键字,并将实现提供为:

class classB :classA
{
    public void sayhello()
    {
        Console.WriteLine("hello I'm B");
    }

};

在这种情况下,可以覆盖该方法。我能够覆盖该方法,它不是虚拟的或抽象的。所以,我的问题是:

1、不违反OOP原则吗?因为我能够覆盖该方法,该方法在父级中未标记为虚拟

2.为什么允许我以这种方式覆盖该方法?哪个甚至没有标记为virtual

3.从classA方法中删除virtual关键字,当我试图在 classB 中覆盖该方法时它给了我在 classA 中密封方法的感觉。(正如我之前提到的编译器错误)。如果我删除virtual,以便子类可能不会覆盖它,那么为什么子类可以巧妙地覆盖它,删除它的override关键字?仅仅是这种情况,sealed关键字是为设计的吗?

4

5 回答 5

3

我想告诉您,您隐藏了未覆盖的父子方法。
您可能没有注意到的另一件事是看到警告,因为在警告部分会明确提到,

警告“行号”'classB .sayhello'隐藏了继承的成员'classA.sayhello'。如果打算隐藏,请使用 new 关键字。

你的问题,

它没有违反 OOP 原则吗?因为我能够覆盖该方法,该方法在父级中未标记为虚拟。

不肯定它没有违反 OOP 原则,因为您隐藏了基类方法。

为什么允许我以这种方式覆盖该方法?哪个甚至没有标记为虚拟?

因为 C# 不仅支持覆盖,还支持方法隐藏,隐藏方法必须使用new关键字声明。有关更多信息,请阅读dotnet_polymorphism和overriding -vs-method-hiding

仅仅是这种情况,sealed 关键字是为设计的吗?

MSDN 中,sealed sealed关键字旨在防止类派生并否定虚拟成员的虚拟方面。

  • 当应用于一个类时,sealed 修饰符防止其他类从它继承。
  • 密封修饰符只能应用于覆盖基类中的虚拟方法或属性的方法或属性。这可以防止进一步覆盖特定的虚拟方法或属性,但它永远不会停止method-hiding。阅读不可覆盖的方法以获取更多信息
于 2013-12-21T10:10:39.160 回答
1

您所做的是方法隐藏(正如其他人已经解释的那样)。

如果您真的想这样做,您应该在方法定义中添加新关键字以使警告消失并作为文档。以便其他查看您的代码的开发人员知道您是故意这样做的。

于 2013-12-21T10:58:45.197 回答
1
  1. 它没有违反 OOP 原则吗?因为我能够覆盖该方法,该方法在父级中未标记为虚拟。

您没有隐藏父方法的父方法overridemethod

所以你永远不能像你的子类那样从子类对象访问父hidden方法method sayhello()

2.为什么允许我以这种方式覆盖该方法?哪个甚至没有标记为虚拟?

因为您可以使用子实现隐藏父方法。

于 2013-12-21T09:53:54.390 回答
1

我认为这来自使用切片的 C++ 实现(什么是对象切片?)。

虽然 C# 主要类似于 Java,但在某些情况下(这个和值类型的存在)它遵循 C++ 方式。

此后的推理是,由于您的代码sayhelloA变量调用方法,因此程序员期望A.sayhello执行的逻辑。它不会违反 OOP 原则,因为您正在执行一个A.sayhello实现(因此它必须与A.sayhello合同相匹配)。

与Java的区别不是OOP与OOP,而是Java使用late binding(实际执行的方法在运行时根据a实际实例决定)而C#使用early binding(方法在编译时决定)除非方法是虚拟的。

我个人更喜欢后期绑定,虽然从 OOP 的角度来看 C# 方法是正确的,但我发现通常应该使用更专业的方法。

于 2013-12-21T10:02:37.703 回答
1

好吧,最后很简单:

  1. 覆盖虚拟方法时,具体方法在运行时解析。
  2. 使用new关键字(或完全忽略它)时,您在编译时根据代码中可用的类型信息执行静态替换操作。

这是两个完全不同的东西。

于 2013-12-21T10:03:24.537 回答