19

在不运行此代码的情况下,确定Foo将调用哪个方法:

class A
{
   public void Foo( int n )
   {
      Console.WriteLine( "A::Foo" );
   }
}

class B : A
{
   /* note that A::Foo and B::Foo are not related at all */
   public void Foo( double n )
   {
      Console.WriteLine( "B::Foo" );
   }
}

static void Main( string[] args )
{
   B b = new B();
   /* which Foo is chosen? */
   b.Foo( 5 );
}

哪种方法?为什么?运行代码不会作弊。

我在网上找到了这个谜题;我喜欢它,我想我会用它作为面试问题......意见?

编辑:我不会判断候选人是否犯了这个错误,我会用它作为对 C# 和 CLR 本身进行更全面讨论的一种方式,这样我就可以很好地了解候选人的能力。

来源: http: //netpl.blogspot.com/2008/06/c-puzzle-no8-beginner.html

4

15 回答 15

57

我真的不会用这个作为面试问题。我知道答案及其背后的原因,但这样的事情应该很少出现,以至于它不应该成为问题。知道答案并不能说明候选人的编码能力。

请注意,即使 A.Foo 是虚拟的并且 B 覆盖它,您也会得到相同的行为。

如果你喜欢 C# 谜题和古怪的东西,我也有一些(包括这个)

于 2008-09-30T17:37:25.257 回答
26

太难?不,但你在这个问题中的目标是什么?您希望从受访者那里得到什么?他们知道这种特殊的语法怪癖吗?这要么意味着他们已经很好地研究了规范/语言(对他们有好处),要么他们遇到了这个问题(希望不是他们写的,但如果他们这样做了 - 哎呀)。这两种情况都没有真正表明你手头有一个可靠的程序员/工程师/架构师。我认为重要的不是问题,而是围绕问题的讨论。

当我面试候选人时,我通常会问一个基于语言语义怪癖的看似简单的问题——但我不在乎我的受访者是否知道,因为语义怪癖让我开辟了很多途径,让我找到如果我的候选人有条不紊,他们的沟通方式,如果他们愿意说“我不知道”,他们是否能够独立思考,他们是否了解语言设计和机器架构,他们是否了解平台和可移植性问题-简而言之,我正在寻找很多加起来就是“他们明白了吗?”的成分。这个过程需要一个小时或更长时间。

最后,我实际上并不关心他们是否知道我的问题的答案——这个问题是一个诡计,让我无需询问即可间接获得所有其余信息。如果你在这个问题上没有有价值的不可告人的东西,那就不要费心去问它——你在浪费你的时间和你的候选人的时间。

于 2008-09-30T18:41:58.860 回答
12

完全同意乔尔的观点。我有 20 多年的设计和编码经验,当我看到它时我想到的第一件事是:它甚至不会编译。

我做出这样的假设是因为我试图避免只有一个数据类型不同的重载,并且在查看代码时甚至没有发现 int/double 的差异;我认为需要一个新的运算符来允许在 B 中重新定义。

事实上,我使用了一个程序员创建的库,用于处理一些文本文件生成,这有点令人困惑,因为其中一个方法有 8 个不同的重载,其中两个仅在最后一个参数的数据类型上有所不同。一个是字符串,一个是字符。参数的字符串版本所需的值是一个字符长的可能性非常好,因此希望您能看到它的发展方向。我们遇到了调试问题,因为库的使用者无意中触发了错误的调用,因为引用差异,单对双。

故事的寓意,感谢候选人不知道答案,因为它可能表明良好的编码习惯。

于 2008-09-30T18:43:38.763 回答
10

另一个投票反对使用它,但出于不同的原因。

当这样摆在现场时,很多真正优秀的程序员会想出错误的答案,但这并不是因为他们不了解这些概念或无法弄清楚。发生的情况是,他们会看到这样的事情并想,“啊哈,诡计问题!”,然后在回答中继续超越自己。在面试环境中尤其如此,他们没有 IDE 或 Google 或任何其他帮助程序员在日常编程中理所当然的帮助。

于 2008-09-30T18:18:29.197 回答
7

Console.WriteLine("B::Foo");

因为它会自动转换并使用第一个而无需进一步继承。

我不认为这是一个很好的面试问题,但尝试在不编译代码的情况下解决可能会很有趣。

于 2008-09-30T17:36:57.597 回答
7

作为一个面试问题并不公平,因为这是一个有点技巧的问题。我想从受访者那里得到的好的答案将更像是“需要重构”

除非您想聘请某人从事编译器工作,否则我不确定您是否需要深入了解 CLR。

对于面试问题,我会寻找能够在回答中显示编码人员理解水平的内容。这更像是一个奇怪/谜题。

于 2008-10-02T12:24:36.100 回答
6

这实际上是一个技巧问题。

对于“应该”发生什么,答案是模棱两可的。当然,C# 编译器将其从具体的模棱两可的领域中解脱出来。但是,由于这些方法相互重载,既不覆盖也不遮蔽,因此可以合理地假设“最佳参数拟合”应该适用于此处,因此得出结论应该是 A::Foo(int n)当提供一个整数作为参数时调用。

为了证明“应该”发生的事情尚不清楚,完全相同的代码在 VB.NET 中运行时会产生相反的结果:

Public Class Form1
    Private Sub Button1_Click(ByVal sender As System.Object, _
                              ByVal e As System.EventArgs) _
                              Handles Button1.Click
        Dim b As New B
        b.Foo(5)   ' A::Foo
        b.Foo(5.0) ' B::Foo
    End Sub
End Class

Class A
    Sub Foo(ByVal n As Integer)
        MessageBox.Show("A::Foo")
    End Sub
End Class

Class B
    Inherits A

    Overloads Sub Foo(ByVal n As Double)
        MessageBox.Show("B::Foo")
    End Sub
End Class

我意识到我正在为 C# 程序员“抨击”VB.NET 不遵守 C# 提供机会。但我认为可以提出一个非常有力的论点,即 VB.NET 在这里做出了正确的解释。

此外,C# IDE 中的 IntelliSense 建议 B 类有两个重载(因为有,或者至少应该有!),但实际上不能调用 B.Foo(int n) 版本(不是没有第一个显式转换为 A 类)。结果是 C# IDE 实际上与 C# 编译器不同步。

另一种看待这个问题的方式是,C# 编译器正在采用预期的重载并将其转换为阴影方法。(这对我来说似乎不是正确的选择,但这显然只是一种意见。)

作为一个面试问题,我认为如果你有兴趣在这里讨论这些问题是可以的。至于“正确”或“错误”,我认为这个问题接近于一个很容易因为错误原因而被遗漏甚至正确的技巧问题。事实上,“应该”这个问题的答案是什么,其实是很值得商榷的。

于 2008-10-01T00:55:13.690 回答
3

这是一个荒谬的问题——我可以使用 Snippet Compiler 在 60 秒内回答它,如果我曾经在依赖于功能的代码库中工作过——它会很快被重构不存在。

最好的答案是“这是一个愚蠢的设计,不要那样做,你不必解析语言规范就知道它会做什么”。

如果我是被采访者,我会非常看好你的极客琐事信誉,并可能会邀请你参加下一场极客琐事追逐。但是,我不太确定我是否愿意与您/为您合作。如果那是您的目标,请务必询问。

  • 请注意,在非正式的情况下,像这样的极客琐事可能会很有趣。但是,对于被采访者来说,采访绝不是有趣或非正式的——为什么还要用被采访者不知道你是否认真对待的琐碎问题来进一步煽动它呢?
于 2008-10-01T03:41:22.933 回答
3

我认为这是一个可怕的问题。当真正的答案是“运行它看看!”时,我认为任何问题都是一个可怕的问题。

如果我需要在现实生活中知道这一点,那正是我要做的:创建一个测试项目,输入它,然后找出答案。然后我不必担心抽象推理或 C# 规范中的细节。

就在今天我遇到了这样一个问题:如果我用同一行填充相同类型的 DataTable 两次,会发生什么?行的一份副本,还是两份?即使我改变了它,它会覆盖第一行吗?我想问问别人,但我意识到我可以很容易地启动一个我已经拥有的使用 DataSets 的测试项目,编写一个小方法并对其进行测试。

回答:

...啊,但如果我告诉你,你会错过重点。:) 关键是,在编程中,您不必将这些东西作为假设,如果可以合理地测试它们,您也不应该这样做。

所以我认为这是一个可怕的问题。你不是更愿意雇佣一个尝试过然后知道会发生什么的开发人员,而不是一个尝试从他逐渐消失的记忆中挖掘出来的开发人员,或者一个会问别人并接受一个答案的开发人员吗?可能是错的?

于 2008-09-30T21:50:31.637 回答
2

这对我来说是一个棘手的问题。如果您使用该语言的时间足够长,那么您可能已经遇到了问题并知道了答案;但多久才够长?

这也是一个问题,你的背景可能对你不利。我知道在 C++ 中 B 中的定义会隐藏 A 中的定义,但我不知道它在 C# 中是否同样有效。在现实生活中,我知道这是一个有问题的领域并尝试查找它。在接受采访时,我可能会尝试猜测,因为我被放在了现场。我可能会弄错。

于 2008-09-30T19:00:23.950 回答
2

仅当您希望候选人在描述他认为应该发生的事情时告诉您他的思维过程时,才可以将其用作问题。谁在乎实际代码的对错——在现实世界中,候选人会发现这样的代码,发现它不起作用并使用调试器和 writeline 调用的组合来调试它,所以如果你想这是一个无用的问题知道(或猜测)正确答案的候选人。

但那些能够解释他们认为会发生什么的人,那是另一回事。即使他们弄错了,也要雇用他们。

于 2008-09-30T19:07:02.887 回答
2

在实际工作情况下,需要 5 秒钟才能找出问题所在。编译,测试,哎呀。

我更关心是否有人可以构建良好且可维护的代码。问诸如此类的问题

  • 你会如何设计这个,写简单的抽象设计,没有代码。
  • 这是一个对象设计。客户来了,说他们想要这个。制定新的设计 - 或 - 讨论需求无法满足实际客户需求的原因。
于 2012-01-25T17:06:26.967 回答
0

尝试下面给出的类似示例:

class Program
{
    class P
    {}
    class Q : P
    {}

    class A 
    { 
        public void Fee(Q q)
        {
            Console.WriteLine("A::Fee");
        }
    }

    class B : A 
    {   
        public void Fee(P p)
        {
            Console.WriteLine("B::Fee");
        }
    }

    static void Main(string[] args) 
    { 
        B b = new B();   
        /* which Fee is chosen? */  

        b.Fee(new Q());
        Console.ReadKey();
    }
}

如果可以隐式转换参数以匹配任何此类方法,编译器似乎更喜欢将“b.Fee()”调用链接到该类型的可用方法而不是继承的方法(基类型的方法)。即参数的隐式转换优先于基类方法链接。

虽然老实说,我发现相反的方法更直观,因为对我来说,继承的方法与直接引入的方法一样好。

于 2008-10-19T06:44:48.767 回答
0

有问题的代码将打印B::Foo

于 2009-10-23T08:24:33.373 回答
0

正如许多人已经说过的那样,当我面试某人时,我想了解候选人是否能够沟通,是否能够编写有意义且易于他人理解的代码,是否可以维护代码等等。我个人不喜欢花时间在建议的练习上。相反,我宁愿做一个练习,让候选人自己编写代码——即使是在普通的文本编辑器中——然后从那里开始基于代码审查、改进等的讨论。无需编译代码:编译器最终会完成它的工作。我知道很多人可能不同意这种方法,但到目前为止,我发现这是寻找优秀候选人的最佳方式。

于 2012-11-03T11:34:04.570 回答