C#潜在面试问题......太难了?

时间:2008-09-30 17:31:34

标签: c# .net puzzle

如果不运行此代码,请确定将调用哪个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本身的更全面的讨论,这样我就可以很好地理解候选人的能力。 / p>

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

15 个答案:

答案 0 :(得分:56)

我真的不会将此作为面试问题。我知道答案及其背后的原因,但是这样的事情应该很少出现,这应该不是问题。知道答案确实没有表明候选人的编码能力。

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

如果你喜欢C#谜题和奇怪,I've got a few too (including this one)

答案 1 :(得分:26)

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

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

最后,我实际上并不关心他们是否知道我的问题的答案 - 问题是让我间接得到所有其他信息而不必询问的诡计。如果你在这个问题中没有一个有价值的问题,那就不要再费心了 - 你在浪费时间和候选人的时间。

答案 2 :(得分:12)

在那里与乔尔不能达成一致。我有20多年的设计和编码经验,当我看到它时,我想到的第一件事是:它甚至不会编译。

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

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

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

答案 3 :(得分:10)

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

当像这样放在现场时,许多真正优秀的程序员会想出错误的答案,但不是因为他们不知道这些概念或无法弄明白。会发生什么事情,他们会看到这样的事情,然后思考,“啊哈,技巧问题!”,然后继续思考自己的反应。在面试环境中尤其如此,他们没有IDE或谷歌或其他任何其他帮助程序员在日常编程中理所当然的好处。

答案 4 :(得分:7)

Console.WriteLine(“B :: Foo”);

因为它将自动转换并使用第一个而不进一步继承。

我认为这不是一个很好的面试问题,但如果不编译代码就可以尝试解决这个问题。

答案 5 :(得分:5)

这实际上是一个棘手的问题。

对于“应该”发生的事情,答案是模棱两可的。当然,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#程序员打开“bash”VB.NET而不遵守C#的机会。但我认为人们可以提出一个非常强烈的论据,即VB.NET正在这里做出正确的解释。

此外,C#IDE中的IntelliSense建议B类有两个重载(因为有,或者至少应该!),但实际上不能调用B.Foo(int n)版本(并非没有先明确地投射到A类。结果是C#IDE实际上与C#编译器不同步。

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

作为一个面试问题,我认为如果你有兴趣在这里讨论这些问题就可以了。至于让它“正确”或“错误”,我认为这个问题接近一个可能很容易错过甚至是正确的技巧问题。事实上,问题“应该是”的答案实际上是非常值得商榷的。

答案 6 :(得分:5)

作为面试问题并不公平,因为这是一个棘手的问题。我希望受访者得到的好答案更多的是“那需要重构”

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

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

答案 7 :(得分:3)

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

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

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

答案:

啊......啊,但如果我告诉你,你会错过这一点。 :)关键是,在编程中你不必将这些东西留作假设,如果可以对它们进行合理的测试,你就不应该

所以我认为这是一个可怕的问题。你不愿意雇用一个开发人员来试试,然后知道会发生什么,而不是一个开发人员试图从他褪色的记忆中挖掘它,或者谁会问别人并接受可能错误的答案?

答案 8 :(得分:3)

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

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

如果我是受访者,我会高度评价你的极客琐事信誉,并且可能会邀请你参加下一场Geek Trivial Pursuit游戏。但是,我不太确定我是否愿意为你工作。如果这是你的目标,请务必提出要求。

  • 请注意,在非正式的情况下,像这样的极客琐事可以是有趣和有趣的。但是,对于一个受访者来说,面试不过是有趣或非正式的 - 为什么进一步用一些琐碎的问题来鼓励他们,如果你认真对待,受访者不知道呢?

答案 9 :(得分:2)

这给我一个棘手的问题。如果你已经使用这种语言足够长的时间,你可能会遇到问题并知道答案;但是多久就够了?

这也是你的背景可能对你不利的问题。我知道在C ++中B中的定义会隐藏A中的定义,但我不知道它在C#中的工作原理是否相同。在现实生活中,我知道这是一个值得怀疑的领域,并试图查找它。在一次采访中,我可能会尝试猜测,已被当场。我可能会弄错。

答案 10 :(得分:2)

只要你期望候选人告诉你他的思考过程,因为他描述了他认为应该发生的事情。谁在乎实际代码的正确与错误 - 在现实世界中,候选人会找到这样的代码,发现它不起作用并使用调试器和写线调用的组合调试它,所以如果你想要它是一个无用的问题知道(或猜测)正确答案的候选人。

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

答案 11 :(得分:2)

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

我更担心的是,是否有人可以构建良好且可维护的代码。提出像

这样的问题
  • 你会如何为此设计,编写简单的抽象设计,无需代码。
  • 这是一个对象设计。客户来了,说他们想要这个。制定新设计 - 或 - 讨论为什么要求无法满足实际客户需求的原因。

答案 12 :(得分: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()”调用链接到类型的可用方法而不是继承方法(基类型的方法)。即隐式转换参数优先于基类方法链接。

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

答案 13 :(得分:0)

相关代码将打印B::Foo

答案 14 :(得分:0)

正如许多人已经说过的那样,当我采访某人时,我想知道候选人是否有能力进行沟通,能够编写有意义的代码,并且可以被其他人轻易理解,可以维护的代码等等。 我个人不喜欢花时间参加提议的练习。 相反,我宁愿做一个练习,让候选人自己编写代码 - 即使是在常规文本编辑器中 - 并从那里开始基于代码审查的讨论,改进等等。不需要编译代码:编译器最终会完成它的工作。 我知道很多人可能不同意这种做法,但到​​目前为止,我发现这是找到好候选人的最好方法。