C# - 调用基类中的方法

3

我有两个类:

public class A
{
    public void WriteLine(string toWrite) { Console.WriteLine(toWrite); }
}

public class B : A
{
    public new void WriteLine(string toWrite) { Console.WriteLine(toWrite + " from B"); }
}

在我的代码中,我执行以下操作:

B writeClass = new B();
writeClass.WriteLine("Output"); // I expect to see 'Output from B'
A otherClass = (A)writeClass;
otherClass.WriteLine("Output"); // I expect to see just 'Output'

我原以为这会起作用,因为涉及到多态性。

然而,每次都会输出“从B输出的结果”。有没有办法让它按照我想要的方式工作?

编辑 修复代码示例。


1
它是 public void WriteLine 而不是 public void Write - Rami Alshareef
1
A 中的方法应该是 WriteLine 而不是 Write 吗? - Russ Cam
1
新的方法只是隐藏了基本实现,因此您仍然可以通过显式调用基类中的方法来调用基本实现。 - Dustin Davis
2
固定的代码示例在LINQPad中对我来说运行良好。输出:B的输出 - StriplingWarrior
2
你发布的代码在我的机器上确实有你期望的输出。如果它在你的机器上没有这个输出,那么你发现了编译器中的一个错误,请发布你正在使用的确切版本号,以便我可以尝试在该版本中重现它。 - Eric Lippert
6个回答

5

当你使用NEW从基类“隐藏”一个方法时,你只是把它隐藏了,仅此而已。当你明确调用基类实现时,它仍然被调用。

A不包含WriteLine,所以你需要修复它。当我修复它时,我得到了:

Output from B
Output


namespace ConsoleApplication11
{
    class Program
    {
        static void Main(string[] args)
        {
            B writeClass = new B(); 
            writeClass.WriteLine("Output"); // I expect to see 'Output from B' 
            A otherClass = (A)writeClass; 
            otherClass.WriteLine("Output"); // I expect to see just 'Output' 
            Console.ReadKey();
        }
    }

    public class A
    {
        public void WriteLine(string toWrite) { Console.WriteLine(toWrite); }
    }
    public class B : A
    {
        public new void WriteLine(string toWrite) { Console.WriteLine(toWrite + " from B"); }
    }
}

1

你在A类上的方法是Write,而不是WriteLine。将其更改为相同的名称,它将按照你的期望工作。我刚试过了,得到了:

B的输出
输出

多态性(C#编程指南)很好地解释了这一点。(这是原帖链接的更新版本。)该页面展示了派生类覆盖虚成员和新成员隐藏基类成员的示例。

关于new修饰符似乎存在一些混淆。从文档中可以看出:

虽然你可以在没有使用new修饰符的情况下隐藏成员,但结果会产生警告。如果你使用new来明确隐藏一个成员,它将抑制此警告并记录派生版本旨在替换基本版本的事实。

请注意,被隐藏的成员不需要是虚拟的。

最佳实践:

  • 强烈建议使用覆盖而不是隐藏。多态调用在面向对象语言中是惯用的。
  • 如果您打算隐藏一个成员,请始终使用new修饰符。
  • 永远不要发布带有编译器警告的代码。
  • 如果您团队中的每个开发人员都同意无法修复编译器警告,请将其抑制。

0

你的A类有一个Write函数而不是WriteLine

public class A
{
    public virtual void WriteLine(string toWrite) { Console.WriteLine(toWrite); }
}

public class B : A
{
    public override void WriteLine(string toWrite) { Console.WriteLine(toWrite + " from B"); }
}

0

在类B中覆盖方法时,不要使用new关键字。并将A中的方法声明为virtual


new 基本上是为了避免编译器警告。它对执行没有任何影响。 - TrueWill
如果他不能将方法设置为虚拟的,因为它不是他的基类可以修改的,那么怎么办?他必须使用New。 - Dustin Davis
@DustinDavis - 他不是必须使用new;他应该使用new。试试看。 - TrueWill
@TrueWill 是的,我知道,但这是不可接受的(至少对于我的团队来说)。我要求使用 New 来明确表明这是我们的意图。 - Dustin Davis
1
@DustinDavis - 我们对此意见一致。 :) 我的观点是关键字不会影响行为。 - TrueWill

0

'new'关键字会使B的WriteLine实现覆盖A的实现。

不要将此作为答案接受,但根据我的经验,在这种情况下使用'new'关键字几乎总是一个错误。它会降低代码的可读性并混淆代码的清晰度。


我同意尽可能避免使用new关键字,但它的存在并没有覆盖实现。它基本上是抑制编译器警告。重新声明方法而不使用virtual/override才是覆盖实现的方式,但这是一种非多态的方式(正如@DustinDavis所演示的)。 - TrueWill
是的,我试图简化行为。但我认为两个有经验的开发人员对于解释其行为的最佳方式存在疑虑,这表明它实际上是多么危险! - Russ Clarke

0
首先:我猜你想把两个方法都命名为“WriteLine”,但是A类中的方法只被命名为“Write”。其次:是的,你从A继承了B,但对象仍然是“B”类型,所以我认为你想要的不可能实现。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接