【问题标题】:confusion regarding overriding rules C#关于覆盖规则 C# 的混淆
【发布时间】:2014-01-10 03:28:14
【问题描述】:

我对方法覆盖和 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 关键字覆盖声明为 virtualabstract(在抽象类中)的方法在儿童班。据此,上面的代码完美无缺。 当我删除 virtual 关键字,然后尝试使用 override 关键字覆盖该方法时,编译器会给出错误:

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

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

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

};

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

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

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

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

【问题讨论】:

  • 你执行了一个叫做方法隐藏的概念msdn.microsoft.com/en-us/library/aa691135%28v=vs.71%29.aspx
  • 是的,但这不是违反 oop 原则吗?比方说,classA 不想让它的方法被隐藏?或者当我们可以简单地隐藏它时,为什么我们然后使用 new 关键字进行阴影?
  • @O.R.Mapper 是的。因为 OP 的实例类型为 classB
  • 在最后一种情况下,您应该看到一条警告,指出您在classB 中引入的方法隐藏了classA 中的方法。这不是压倒一切的。它只是两个不相关的同名方法。避免这种情况;这会导致混乱。
  • @JeppeStigNielsen 是的,我注意到警告:) 无论如何,我可以确定,子类不会这样做?

标签: c# oop virtual-functions overriding sealed


【解决方案1】:
  1. 没有违反OOP原则吗?因为我能够覆盖该方法,该方法在父级中未标记为虚拟。

你没有overridemethod你隐藏了父方法。

因此您永远无法从子类对象访问父方法,因为它是您的子类method sayhello()hidden

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

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

【讨论】:

  • 谢谢。我可以确定,我的子类永远不会以这种方式隐藏父类的方法吗?因为这似乎是一件危险的事情。
  • @Zeeshan:唯一的可能是检查警告:Warning 1 'classB.sayhello()' hides inherited member 'classA.sayhello()'. Use the new keyword if hiding was intended.
【解决方案2】:

我认为这来自使用切片的 C++ 实现 (What is object slicing?)。

虽然 C# 与 Java 很相似,但在某些情况下(这一点以及值类型的存在)它遵循 C++ 的方式。

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

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

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

【讨论】:

  • 很好的解释和区分:)
  • 请注意 a 是 B 类的对象。很抱歉没有造成混乱。
  • 当父类方法没有被标记为virtual并且要求在子类中覆盖它,那么我唯一能做的就是隐藏。仪式?我的意思是,为了获得压倒一切的效果
【解决方案3】:

嗯,最后还是挺简单的:

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

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

【讨论】:

  • 谢谢。你的意思是当我隐藏方法的实现时,这种情况下的解决方案实际上是在运行时?
  • 不,反过来:重写意味着在运行时解析,方法隐藏在编译时发生。
  • 是否覆盖总是在运行时解决?即使是静态绑定,代码中的 0% 多态效应?
  • 是的,总是。那是因为以后总是可以从外部覆盖虚拟方法。您无法在编译时预测这一点。 OO 多态性的整个概念假设在编译时没有可用的具体类型信息。
  • 对。 覆盖一个非虚拟方法是不可能的。你只能隐藏它(但是你会失去多态性)。
【解决方案4】:

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

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

你的问题,

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

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

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

因为C#不仅支持覆盖,还支持方法隐藏,而且隐藏方法必须使用new关键字声明。欲了解更多信息,请阅读 dotnet_polymorphismoverriding-vs-method-hiding

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

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

  • 当应用于一个类时,sealed 修饰符会阻止其他类从它继承。
  • 密封修饰符只能应用于覆盖基类中的虚拟方法或属性的方法或属性。这可以防止进一步覆盖特定的虚拟方法或属性但它永远不会停止method-hiding。阅读Non-overridable method了解更多信息

【讨论】:

  • 谢谢 :) 你能说清楚吗,不是隐藏,间接等于覆盖吗?当父类不希望它的方法被覆盖时,为什么要让这种情况发生?
  • @Zeeshan 查看编辑后的答案以获取更多信息,您可以通过overrideSealed Overriden 找到有关sealed 的更多有趣想法
  • 你的意思是,我不能影子密封的方法?
【解决方案5】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-12-13
    • 1970-01-01
    • 1970-01-01
    • 2013-11-30
    • 1970-01-01
    • 2018-04-30
    • 2010-11-24
    • 1970-01-01
    相关资源
    最近更新 更多