.NET面试题系列[0] - 写在前面

时间:2022-08-26 09:07:06

.NET面试题系列目录

经过了四年的工作,我已经跳槽6次了。这四年我全部都使用C#进行开发,除了获得到的offer之外,还面试失败或拒掉了不少offer,加起来面试的次数至少有30次。这些面试有质量很高的,也有泛泛而谈的,不同面试有时候还会问到几乎相同的问题,通过对问题的深入程度,可以大致判断一家公司和面试官的水平。

跳槽就是为了更好的生活。而改善生活需要有强大的业务能力,说服面试官在众多候选人中选择你作为胜利者。在若干年的工作学习中,我的水平也慢慢上升,一开始是什么都不会,后来会一些东西,到现在也可以从面试官的问题中,大概了解到他/她的水平如何。

不同类型的公司的面试题目也略有不同,而这些题目要不代表着你面试通过之后可能在实际工作中遇到的问题,要么就是面试官希望通过题目了解你个人的水平如何。例如Facebook,Google,BAT这种综合性公司在校招的时候,面对的都是应届大学生,他们没有什么可能会对搜索引擎十分专精,或者擅长自然语言语义分析。他们会的只是四年的大学计算机教育而已,面试完了之后,这些胜出者也不知道自己会去哪个组做什么方向的工作。所以这时候,给他们出题也不能考搜索引擎,大数据,而只能考最general的内容,那无非就是计算机基础知识和算法了。

而社招大部分情况下,面试官希望你第一天面试通过,第二天就能上班,并且来之能战。所以这时候问题基本都是你将来工作可能会遇到的问题。面试官希望你研究过这些问题,并且证明自己能做的比别人更好。当然,面试官也希望你的基础知识水平牢固,不会在开发,部署,测试等基本流程上一问三不知。例如,我曾经面试过三家投行,他们都希望面试者熟悉WPF或者Winform。所以,其中的两家问到了工作线程如何修改UI线程控件的内容这个经典问题。对于这个经典的情境,你不仅需要知道如果是按照常规的方法处理会发生什么(UI线程控件的内容只能被拥有控件的线程修改),还要知道怎么解决(Winform使用委托,WPF使用Dispatcher)。而且,你还可以提,如果你的应用是ASP.NET Web Application,则你需要反其道而行之,使用ConfigureAwait强制令系统不进行任何线程的切换。如果面试官知道你在说什么,他/她一定会很开心。如果面试官表现的一脸懵比,你可以在心中为这家公司降低一个档次。

当你回答问题时,如果你可以答的超乎面试官的意料,则他们给你开的工资可能也会出乎你的意料。例如,对于简单的问题“如何实现一个单例模式”,如果你只是给出了很简单的一个构造函数,则面试官可能会觉得你对线程安全不够敏感(当然比较弱的面试官可能会很高兴) 。如果你加了双重锁检查,面试官会问你第一个if是做什么的。如果你也知道,并且还可以解释清楚,为什么需要双重锁检查,面试官会觉得很满意。如果你甚至还可以提一句,在很多依赖注入工具中,只需要一个函数就能保证对象在全局都是单例的,例如autofac就有这个功能。面试官将会非常爽,然后话题可能会转到ioc,或者谈谈天气。

外国人问问题有时候不按常理出牌(或者你可以理解为,外国人的常理就是那样)。我遇到过的比较有趣的问题有:说出你觉得c#好的任意一个理由(我给出的答案是它的语法自然易懂,且比较优雅,背后编译器作的大量工作你可以不用知道。例证如lambda表达式和async与await关键字),为什么c语言被命名为c,说出你自学某语言时看过的书籍,你最喜欢哪种数据结构等。这些问题没有标准答案,你需要结合事例解释你的答案。对付这种问题,死记硬背是没有什么用的,你需要理解或者在背书的基础上,形成你自己的观点。

在学习的过程中,我泛泛阅读了若干本经典书籍,当然还有很多本正放在书架上供着,我可能一辈子也不会去看或者理解的了(例如“编译原理”)。我也看了很多博客,其中有大量现成的代码可供直接使用。我认为,作为一个立志奋发图强的.NET + C# 程序员,应该了解的知识包括:

  1. .NET框架基础知识,如.NET框架的核心 – CLR做什么,.NET是如何将源语言编译成目标语言的,.NET中程序集的作用等等。这些都是背景知识,除了程序集之外基本没人问,但合格的. NET程序员必须知道。如果你愿意了解更多,可以去拜读“龙书”(即编译原理)。我反正在5年之内不打算读这本书。
  2. C#的基础知识,如类型安全,类和结构,垃圾回收,反射,面向对象基本知识等。这些问题比较基础,但如果深入的问起来还是可以难倒很多很多开发者的。通读一遍CLR via C#会有很大帮助。
  3. 委托与事件。虽然任何一本书上都有关于它们的章节,但仍然有很多人对委托的理解仅仅是函数指针这个层次。熟悉委托和事件的开发者,应当知道两者之间的关系,什么是委托链,以及委托是如何做到无处不在的(例如,linq里面就大量充斥着委托)。
  4. C#从2.0开始的新特性,这是一个极大的话题,主要包括2.0的泛型和可空类型,匿名方法,3.0的linq(以及相关的所有特性,包括闭包),4.0的dynamic,泛型的协变与逆变,4.5的async & await等等。熟悉它们会对代码有质的影响。任意一本好的介绍C#的书籍都会详细介绍它们,我个人则是看了Jon Skeet的深入理解C#。这本书包含了很多技术细节,我有三分之一都看不懂。不过个人认为,是否理解那些细节并不会影响你的代码质量。通常来说,敢在简历上写“熟悉C#”的人,至少应该了解三分之二的C#新特性。
  5. 数据结构。C#帮你实现了很多常用的数据结构,包括队列,栈,链表。对一些比较常用的数据结构要做到熟悉,并在特定情境时有所取舍。例如,对于经常在中间插入删除,但没有什么查找的情境时,选用链表而不是List<T>。C#所有的数据结构都基于IEnumerable,我曾经遇到一个面试官问我IEnumerable是做什么的,怎么实现一个IEnumerable。个人认为,后一个问题有点强人所难。不过,如果有人问我List<T>和IList<T>有什么区别,我不会觉得很过分。清华大学出版的“数据结构”足以让你在这个话题所向披靡。
  6. 多线程的基本知识。进程和线程的区别,线程的优劣,以及线程的状态,如何管理线程。对这个问题的深入可以去到计算机最基础的构造。在C#中,CLR提供了线程池管理线程,不过我们不需要直接和它进行沟通,我们可以通过任务间接的和它沟通。线程有几种状态:start,工作中,被阻塞和abort。它们之间的互相转化构成了同步的几种方式。你可以去搞一本“深入理解计算机系统”,也许你会对这个问题豁然开朗,不过我才刚刚开始这本书的阅读。
  7. 多线程同步。同步方式有很多种,从最经典的lock到它的真身monitor,mutex,semaphore,Event构造,原子操作,死锁等。考这个的面试官基本都是高手,因为明白这一块内容很不容易。我面试了这么多家公司,也才就遇到三个问这块问题的,而且只有一个问的十分深入,剩下的只是考考概念。不过,对于死锁,很多人喜欢问,他们通常问死锁产生的条件以及如何避免。加里.纳特的“操作系统”中对于死锁有着十分精彩的论述。通常可以结合哲学家就餐问题回答如何避免死锁。Windows核心编程这本书也很好,不过我也还没开始看。
  8. 异步编程的基本知识。首先,要清楚异步和多线程没什么关系,它并不是多线程的子集。单线程也可以异步。然后,C#对异步操作提供了几种方式,例如基于委托,基于事件,以及基于任务。对于每种方式,要清楚它的优缺点(通常缺点都是难以取消或者获得结果,而基于任务的方式解决了所有问题)。
  9. 测试。如何编写单元测试和集成测试,测试的作用,如何隔离和模拟物件,测试驱动开发。不会测试或者不有意在工程中添加测试代码的工程是令人不安的。持续集成重要的一个原因就是它可以帮你自动把测试都跑一遍。一个无懈可击的测试十分十分复杂。
  10. 软件工程方面的知识。包括版本控制,持续集成等。持续集成已经越来越多地被各个公司使用,以实现快速迭代和交付。如果你可以独立为公司实现持续集成的从无到有,你将会十分值钱。另外,你之前的公司是实行什么风格的软件开发流程的?是瀑布还是敏捷?你更喜欢哪种?你对结对编程有什么看法?你认为敏捷有什么不好的地方?
  11. 关于数据库的一些基本知识,包括几个范式,最基本的SQL,表上查询需要注意的事情(索引怎么放,放哪列等)。另外,你可能也需要知道ORM(Entity Framework)的好处有哪些。大数据,商业智能方向以及数据库管理员需要重点了解这块,并且还要加上大量内容,例如事务,数据库容灾,备份,数据仓库以及ETL。对于不是这些方向的开发者则不需要了解。梦里花落知多少的这篇http://www.cnblogs.com/anding/p/5281558.html其实已经有点多了,个人觉得(当然,对于DBA来说又太少了)。尤其是对于那些把数据都放在云上,平时拿数据都通过API的互联网金融公司来说。对于那种公司,你不会SQL都没关系,因为根本不需要,你也不用建表。
  12. 常用设计模式。虽然经典的设计模式有20多种,但没人会变态到让你把它们都背下来。最常见的无非是单例模式,迭代器模式,工厂,策略模式(ioc),观察者模式等等,清楚它们怎么实现,什么时候使用它们。另外,对于solid要大致了解,并且在自己设计时,尽量做到。要了解ioc究竟有什么好处。
  13. 代码风格和简单算法。要说看一个人的编程水平如何,最准确的方法就是看代码了。好的代码让你赏心悦目,差的代码把你逼上梁山。代码必须要清晰易懂,配上精准的注释,以至于让别人基本不需要思考就能理解你要做什么。我非常痛恨的代码有:一个函数超过100行,命名混乱,瞎选数据结构,算法喜人等。对于算法,如果不是面算法工程师,会最基本的几个排序和递归问题就足够了,例如,如果你知道C#的Sort方法是如何实现的,并且理解为什么会这么干,或者你知道数据库的索引用的是B+树而不是哈希,你的算法水平已经足够了。(对于谷歌第一题就是徒手翻转二叉树我表示不对胃口)不过如果有兴趣,可以读一读这本“算法”(不是算法导论)https://book.douban.com/subject/10432347/。如果你没有在大学看过算法导论的话,那么算法导论只有一个功能,那就是可以成功证明你的数学是*。如果你声称你能看懂TAOCP的第一本,请做一个for循环,给世界前10科技公司和投行各发一份简历,请相信我,如果你连TAOCP的第一本都看得懂,英语对你的大脑来说简直就是没有难度,你甚至只需要写一句话:我能看懂TAOCP的第一本。
  14. 能用任意一款脚本语言写一个猜数字(就是文曲星上的那个游戏)。脚本语言是生活的小帮手,尤其是你需要批处理运行多个程序的时候。我们不需要做到十分熟悉Linux的各个命令,但我们可以在Python,Perl等脚本语言中任选一款,写一些你认为用C# + VS过于笨重的小程序。当然,如果你还擅长函数式编程语言,那简直太棒了。
  15. 你喜欢使用什么工具,让你的茫茫重构之路变得不那么崎岖?我个人则是一直在用Resharper。

上面这些如果你基本都会的八九不离十,你在任何一家公司都可以胜任一个后端程序员了。但如果你面试的职位还需要你写点API,以便让前端的哥们使用,那你还要粗浅的了解以下内容:

1. Web基础,包括五层模型,get和post的区别,http协议的一些状态码以及IIS的基本设定。对这个话题的深入了解,可以搞一本计算机网络方面的书籍,里面有详细的关于五层模型,TCP和IP协议的细节。如果你不是应聘网络方面的专家级别职位,我不认为它们是十分必要的。当然,如果你立志成为这方面的专家,请遵循http://coolshell.cn/articles/4990.html

2. Web服务。Web服务具有悠久的历史,最经典的两种方式SOAP和Restful的区别要知道。另外,微软对于这两种方式都给出了自己的实现,即WCF和WebAPI。

3. 对MVC有一定的了解。了解如何利用工具,对你的API进行自动化或非自动化的集成测试。

4. OWIN。微软的OWIN帮助我们从ASP.NET又臭又长的生命周期解脱出来,从此我们可以定义自己的生命周期。新的ASP.NET Core中也是这样,生命周期已经成为历史。我们要了解OWIN基本的工作方式,以及它如何和ioc工具进行配合。另外,通过OWIN,我们的工程可以和IIS解耦,可以用其他的工具代替笨重的IIS。

5. 对于金融交易系统,需要特别关注一下SignalR。它集成了服务器和用户进行通讯的几种方式,从常规的轮询,到服务器主动发送讯息,以及现在常用的WebSocket。它会根据情况自行选用它认为最合适的方式。

上面的内容并不包括前端,这是因为我觉得全栈工程师十分不现实(因为前端的内容和后端几乎是一样多的,而光掌握后端的内容,也需要至少3-5年的时间),所以不打算朝这个方向努力,而且我对前端的了解也十分皮毛。当然,只会简单的HTML和jQuery,可不能称自己是一个合格的前端工程师。

在漫漫的学习过程中,我们不仅要打牢基础,还要时刻关注新技术的动向。C# 6推出来一段时间了(虽然其中并没有什么重大的改进),C# 7也马上就要问世。ASP.NET Core的推出,把前端的打包和测试拉上了台面,同时令Angular JS 成为官方前端语言。对于后端来说,也许你没有机会为公司贡献Angular JS代码,但把握住新技术的演进流程,也会对整个知识体系的融会贯通有重要的作用。

我已经写了不少笔记,虽然大部分都是抄别人的。我在这里也打算效仿博客园众位前辈,弄一个面试题系列,其中不完全包含上述的所有内容,望各位大大不吝赐教。毕竟互相交流,人人都能受益。那么废话少说,我们马上开始。