标题：.NET程序员面试宝典
.NET程序员面试宝典
出版社：电子工业出版社
作译者： 欧立奇 
ISBN号： 978-7-121-06540-8 
出版日期： 2009-01 
丛书名： 程序员面试宝典
字数：456千字 
定价：￥39.80元
内容简介
《.NET程序员面试宝典》取材于各大IT公司历年面试真题（笔试、口试、电话面试、英语面试，以及逻辑测试和智商测试），详细分析了应聘程序员职位的常见考点。《.NET程序员面试宝典》分为6部分，主要内容包括：求职过程，C#程序设计，数据结构和软件工程，UNIX、Oracle和网络，.NET扩展项目，综合面试题等。最后《.NET程序员面试宝典》着力讲述了如何进行英语面试和电话面试，并给出了大量实际英语面试中的问题、参考答案及常用词汇，尝试解决程序员应聘外企时语言问题造成的瓶颈。《.NET程序员面试宝典》的面试题除了有详细解析和回答外，对相关知识点还有扩展说明。希望真正做到由点成线，举一反三，对读者从求职就业到提升计算机专业知识都有显著帮助。
《.NET程序员面试宝典》适合计算机相关专业应届毕业生阅读，也适合作为正在应聘软件行业的相关就业人员和计算机爱好者的参考书。 
编辑推荐
揭开知名IT企业面试、笔试的核心机密，传授程序员岗位求职的关键技巧，传递快乐工作的精神与态度。
《.NET程序员面试宝典》涉猎各大IT公司历年面试真题（包括笔试题、口试题、电话面试、英语面试，以及逻辑测试和智力测试），通过精确详细的分类，把在应聘程序员（含网络、测试等）过程中所遇见的常见考点为你一一点破。
许多面试看似简单
却需要深厚的基本功才能给出完美的答案
我们能真正写好一段C#归并排序代码吗？
我们都觉得自己能
可是我们写出的代码很可能只能拿到10分中的2分……
《.NET程序员面试宝典》将告诉你如何提高自身功力
以从容面对企业的面试
《.NET程序员面试宝典》要点：
求职过程
．NET程序设计
数据结构与设计模式
操作系统，数据库与网络、软件测试
英语面试，电话面试与智力测试
如果你的简历上写着：“本人精通C#编程、．NET体系；熟悉设计模式、数据结构。”并且自认足够机智，还能在工小时内对下列问题给出准确的答案．那么．毫无疑问你可以通过多数，NE丁程序员面试了。
(1)const和staticreadonly的区别是什么？
(2)XmlSerializer如何工作？
(3)静态构造函数是否可使用this？
(4)实现C#归并排序。
(5)写一个事件：饮水机没水时，A或B或其他人给它加水。
(6)“不要和陌生人说话”是设计模式中哪种原则的通俗表达？
(7)解释UML中的4个名词：依赖、聚合、泛化和关联。
(8)对象实现了Finalize将对垃圾收集有何影响？
(9)描述你对．NETFramework2．0中匿名方法的认识。
(10)1000瓶药水至多有l瓶含剧毒．给你10只狗，在24小时内通过给狗试药的方式找出哪瓶药水有毒或者全部无毒(狗服药X小时后毒发，19<X<23)。
如果，你还不能对上面大部分题目立刻给出准确的答案，甚至还茫无头绪，那或许《.NET程序员面试宝典》将有效弥补你在．NET程序员面试中的缺项，并构造完备的知识体系。
目录
第一部分：求职过程 - 4 -
第1章：应聘求职 - 4 -
1.1【渠道】 - 5 -
1.2【流程】 - 5 -
1.3【职位】 - 6 -
第2章：求职五步曲 - 7 -
2.1【笔试】 - 7 -
2.2【电话面试】 - 11 -
2.3【面试】 - 11 -
2.4【签约】 - 12 -
2.5【违约】 - 17 -
第3章：简历书写 - 18 -
3.1【简历注意事项】 - 18 -
3.2【简历模版】 - 19 -
第4章：职业生涯发展规划 - 24 -
4.1【缺乏工作经验的应届生】 - 24 -
4.2【更换工作的程序员们】 - 26 -
4.3【快乐的工作】 - 26 -
第二部分：C#程序设计 - 28 -
第5章：C#程序设计基本概念 - 28 -
5.1【常见概念】 - 29 -
5.2【C++和C#区别】 - 31 -
5.3【数据类型】 - 31 -
5.4【extern】 - 39 -
5.5【其它】 - 39 -
第6章：const异常 反射 - 40 -
6.1【const】 - 40 -
6.2【异常】 - 42 -
6.3【反射】 - 44 -
第7章：传递与引用 - 49 -
7.1【传值】 - 50 -
7.2【静态变量与私有变量】 - 51 -
7.3【输入输出流】 - 51 -
7.4【程序集】 - 52 -
7.5【序列化】 - 53 -
第8章 循环，条件，概率面试例题 - 54 -
8.1【foreach】 - 54 -
8.2【递归与回朔】 - 56 -
8.3【条件语言】 - 60 -
8.4【随机数】 - 61 -
第9章：关于面向对象的面试问题 - 63 -
9.1【面向对象的基本概念】 - 63 -
9.2【访问修饰符】 - 65 -
9.3【类和结构】 - 67 -
9.4【Static】 - 72 -
9.5【密封类】 - 75 -
9.6【构造函数和析构函数】 - 76 -
9.7【虚函数】 - 78 -
9.8【静态构造函数与私有构造函数】 - 78 -
9.9【多态的概念】 - 82 -
9.10【索引器】 - 85 -
第10章：继承与接口 - 86 -
10.1【继承基础问题】 - 86 -
10.2【关于new】 - 91 -
10.3【this问题】 - 96 -
10.4【base问题】 - 99 -
10.5【抽象类】 - 101 -
10.6【接口】 - 108 -
10.7【其它】 - 111 -
第11章：委托，事件，泛型 - 117 -
11.1【委托】 - 117 -
11.2【事件】 - 127 -
11.3【泛型】 - 130 -
第二部分：数据结构和软件工程 - 132 -
第12章：数据结构 - 132 -
12.1【链表】 - 132 -
12.2【栈和堆】 - 134 -
12.3【树】 - 135 -
第13章：字符串 - 144 -
13.1【字符串基础】 - 144 -
13.2【StringBuilder】 - 145 -
13.3【正则表达式】 - 146 -
13.4【数组】 - 147 -
13.5【字符串其他问题】 - 148 -
第14章：排序 - 150 -
14.1【排序基础知识】 - 150 -
14.2【冒泡排序】 - 153 -
14.3【鸡尾酒排序】 - 156 -
14.4【选择排序】 - 157 -
14.5【插入排序】 - 159 -
14.6【希尔排序】 - 160 -
14.7【快速排序】 - 163 -
14.8【归并排序】 - 167 -
14.9【堆排序】 - 170 -
第15章：设计模式 - 172 -
15.1【基础模式】 - 173 -
15.2【结构模式】 - 181 -
15.3【Clone】 - 184 -
15.4【观察者模式】 - 187 -
15.5【UML】 - 189 -
15.6【软件工程】 - 192 -
第16章：软件测试 - 194 -
16.1【白盒测试】 - 195 -
16.2【性能测试】 - 200 -
16.3【关于游戏】 - 200 -
第三部分：Unix，Oracle，网络 - 202 -
第17章：操作系统面试例题 - 202 -
17.1【进程】 - 203 -
17.2【线程】 - 207 -
17.3【UNIX】 - 209 -
17.4【Windows】 - 212 -
第18章：数据库和SQL语言 - 213 -
18.1【数据库理论】 - 213 -
18.2【ORACLE基础】 - 215 -
18.3【SQL语言客观题】 - 217 -
第19章：计算机网络及分布式系统 - 222 -
19.1【网络结构】 - 222 -
19.2【TCP/IP】 - 223 -
19.3【网络安全】 - 227 -
19.4【网络其他】 - 228 -
第四部分：.NET扩展项目 - 230 -
第20章 Winform窗体与控件 - 231 -
20.1【窗体类】 - 231 -
20.2【控件类】 - 232 -
20.3【图形】 - 232 -
第21章 ADO数据库相关 - 233 -
21.1【ASP.NET基础】 - 234 -
21.2【ADO.NET体系结构】 - 235 -
21.3【数据绑定】 - 237 -
21.4【Datagrid】 - 239 -
21.5【Dataset】 - 240 -
第22章 .NET中Web设计 - 241 -
22.1【Session】 - 242 -
22.2【Cookie】 - 244 -
22.3【XML】 - 245 -
22.4【数据传递】 - 247 -
第23章ASP.NET应用技术 - 249 -
23.1【.NET 局域网】 - 250 -
23.2【.NET Remoting体系】 - 251 -
23.3【Webservice】 - 254 -
第24章 .NET性能与安全 - 255 -
24.1【内存管理】 - 255 -
24.2【垃圾管理】 - 256 -
24.3【.NET安全设置】 - 258 -
第五部分：综合面试题 - 258 -
第25章 英语面试 - 259 -
25.1【面试过程和技巧】 - 259 -
25.2【About job】(关于工作) - 260 -
25.3【About person】(关于个人) - 263 -
25.4【About future】(关于未来) - 266 -
第26章 电话面试 - 267 -
26.1【电话面试之前的准备工作】 - 267 -
26.2【电话面试交流常见问题】 - 268 -
第27章 智力测试 - 274 -
27.1【关于数字的智力问题】 - 274 -
27.2【关于推理的智力问题】 - 277 -
27.3【关于综合的智力问题】 - 280 -
27.4【关于群体面试】 - 281 -
第28章 逻辑测试 - 284 -
28.1【文字逻辑】 - 284 -
28.2【图形逻辑】 - 286 -
28.3【找规律】 - 288 -
28.4【关于表格】 - 290 -
附录 面试经历总结 - 298 -
本书样张如下：
5.5【其它】
面试例题1：编写一段代码，其功能是打印代码本身。（要完全使用程序代码生成一段和自己的代码一模一样的字符串）。[欧盟著名通讯公司N面试题，2008]
解析：一道很有趣的面试题，就好像要求一个魔术师，扯着头发把自己拔起来的感觉。本题在编写时要注意计算代码长度。
答案：用C#编写代码如下：（仅一行）
class P { static void Main() { string s = "class P{{static void Main(){{string s=;System.Console.WriteLine(s,s,(char)34);}}}}"; System.Console.WriteLine(s, s, (char)34); } }
6.1【const】
面试例题1：const 和 static readonly 区别是什么？
解析：
这个问题虽然很简单，但有时候也能困扰我们。const和static readonly的确很像，都在程序中只读，都是一旦初始化则都不再可以改写，都是属于语言的静态。并且在多数情况下可以混用。
两者区别是这样的，关于const
1.在编译期间解析的常量；
2.必须在声明就初始化；
3.既可用来修饰类中的成员，也可修饰函数体内的局部变量。
而关于static readonly
1.在运行期间解析的常量；
2.既可以在声明时初始化，也可以在构造器中初始化；
3.只可以用于修饰类中的成员
这里有几个例子：
static readonly MyClass myclass = new MyClass();
//必须使用static readonly,因为new需要在运行时确定
//而const只能必须在编译时就确定
static readonly A = B * 20;
static readonly B = 10;
//可以使用static readonly,显然可以在运行时确定语句
//也可以使用const// const A = B * 20;
// const B = 10;
//编译器会在编译时候，就把B编译为10，A编译为200，而不是在运行时再计算B * 202.
下面有一个项目，有一个MyInt的属性，定义如下：
public class MYClass
{
public const int MyInt = 5;
public static readonly String strStaticReadonly = "StaticReadonly";
}
另一个项目中引用该属性
public class AnotherClass
{
int I = MYClass.MyInt;
string kk = MYClass.strStaticReadonly;
}
编译运行程序, I = 5；kk = StaticReadonly。这是当然的。但是如果我们改变MYClass中的MyInt和strStaticReadonly的值：
public class MYClass
{
public const int MyInt = ; //这里改为6
public static readonly String strStaticReadonly = "changed";//这里改为changed
}
然后编译MYClass所在的项目，生成dll。再运行程序AnotherClass（只是运行，而不是编译后运行），发现I还是= 5，而不是6！，但是strStaticReadonly却变成了changed。为什么会这样？因为在AnotherClass中，I已经被定义为5而不是运行时再去取dll的值，所以说const是编译时就唯一确定了。
答案：C#拥有两种不同的常量：静态常量(compile-time constants)和动态常量(runtime constants)。它们有不同的特性，错误的使用不仅会损失效率，还可能造成错误。相比之下，静态常量在速度上会稍稍快一些，但是灵活性却比动态常量差很多。
//静态常量(隐式是静态的)
public const int compiletimeConstant = 1;
//动态常量
public static readonly runtimeConstant = 1;
静态常量在编译时会将其替换为所对应的值，也就是说下面这2句话通过编译器编译后产生的中间语言是一样的。
//通过编译后二者会被翻译成相同的中间语言
int myNum = compiletimeConstant;
int myNum = 1;
动态常量的值是在运行时获得的。编译器将其标为只读常量，而不是用常量的值代替。静态常量只能被声明为简单的数据类型(内建的int和浮点型)、枚举或字符串。下面的程序段是通不过编译的。你不能用new关键字初始化一个静态常量，即便是对一个值类型来说。
//这样是错误的
public const DateTime myDateTime = new DateTime(2006,9,1,0,0,0);
//这样是可以的
public static readonly DateTime myDateTime = new DateTime(2006,9,1,0,0,0);
只读数据也是常量的一种，它们不能在构造器初始化之后被修改。但是它同静态常量不同，它的值是在运行时才被指派的，因此就会获得更大的灵活性。动态常量可以是任意的数据类型。二者最大的差别在于：静态常量在编译时会将其换为对应的值，这就意味着对于不同的程序集来说，当你改变静态常量的时候需要将其重新编译，否则常量的值不会发生变化，可能引发潜在的问题，而动态常量就不会有这种情况。用const定义的常量(隐式是静态的)，需要像访问静态成员那样去访问const定义的常量，而用对象的成员方式去访问会出编译错误。 声明的同时要设置常量值。从另一方面来说，如果你的确要声明一些从不改变且处处唯一的常量，例如钩子函数SetWindowsHookEx的idHook参数或序列化时的版本等，就应该使用静态常量。但是用到这样的常量的机会不多。一般来说我们应该使用灵活性更高的动态常量。
两者区别如下：
静态常量 动态常量
内存消耗 无 因为要保存常量 有消耗
初始化 很少的简单类型；不能new，必须在声明同时赋值 任意类型，可以在类构造函数中赋值
何时发挥作用 编译时进行替换 相当于类中的数据成员
11.2【事件】
面试例题1：写一个事件，公司饮水机没水时候，小王、小李或者公司其他同事给引水机加水？
解析：本题主要考校观察者模式，观察者模式中主要包括如下两类对象： 
1. Subject：监视对象，它往往包含着其他对象所感兴趣的内容。在本范例中，饮水机就是一个监视对象，它包含的其他对象所感兴趣的内容，就是waterTankState字段，当这个字段的值为0的时候，会不断把数据发给监视它的对象。 
2. Observer：监视者，它监视Subject，当Subject中的某件事发生的时候，会告知Observer，而Observer则会采取相应的行动。在本范例中，Observer就是员工，它们采取的行动分别是Drink和FillWater。 
在本题中，事情发生的顺序应该是这样的： 
1. 员工告诉饮水机，它对它的容积比较感兴趣(注册)。 
2. 饮水机知道后保留对员工的引用。 
3. 员工到饮水机前进行饮水这一动作，当容积为0，通过引用，自动调用Fillwater()方法。 
类似这样的例子是很多的，GOF对它进行了抽象，称为观察者模式：观察者模式是为了定义对象间的一种一对多的依赖关系，以便于当一个对象的状态改变时，其他依赖于它的对象会被自动告知并更新。观察者模式是一种松耦合的设计模式。
答案: 详细代码如下:
// Observer pattern -- Structural example 
using System;
//Delegate 加水委托
delegate void fillwaterDelegate();
//ConcreteWaterTank 创建水箱类
class ConcreteWaterTank 
{
// Fields
private int subjectCubage; //当前容积
public event fillwaterDelegate fillwaterHandler; //建立加水事件
// Properties
public int waterTankCubage
{
get { return subjectCubage; }
set { subjectCubage = value; }
}
// Methods
public void Attach(fillwaterDelegate ud)
{
fillwaterHandler += ud;
}
public void Detach(fillwaterDelegate ud)
{
fillwaterHandler -= ud;
}
public void Notify()
{
if (fillwaterHandler != null) fillwaterHandler();
}
}
// "ConcreteEmployee" //建立员工类
class ConcreteEmployee
{
// Fields
private string employeeName; //员工姓名
private int observerCubage; //关注水箱状态
private ConcreteWaterTank subject; 
// Constructors //建立构造函数
public ConcreteEmployee(ConcreteWaterTank subject,
string employeeName)
{
this.subject = subject;
this.employeeName = employeeName;
}
// Methods
public void fillWater()
{
if (subject.waterTankCubage == 0)
//关注水箱 如果水箱没水 则注水
{
Console.WriteLine("Employee go to waterTank and find the water is Null", employeeName);
subject.waterTankCubage = 10;
observerCubage = subject.waterTankCubage;
Console.WriteLine("Employee fill water to waterTank.Now waterTank's state is ", employeeName, observerCubage);
subject.waterTankCubage = subject.waterTankCubage - 1;
observerCubage = subject.waterTankCubage;
Console.WriteLine("After do that he drink, Now waterTank's state is ",employeeName, observerCubage);
}else
{
//否则 则饮水
subject.waterTankCubage = subject.waterTankCubage - 1;
observerCubage = subject.waterTankCubage;
Console.WriteLine("Employee go to waterTank, and he drink, Now waterTank's state is ",employeeName, observerCubage);
}
}
// Properties
public ConcreteWaterTank waterTank
{
get { return subject; }
set { subject = value; }
}
}
// "ConcreteEmployee" //其他观察者 他们仅是观察但不动作
class AnotherObserver
{
// Methods
public void Show()
{
Console.WriteLine("AnotherObserver got an Notification!");
}
}
public class Client
{
public static void Main(string[] args)
{
ConcreteWaterTank s = new ConcreteWaterTank ();
//建立水箱s
ConcreteEmployee o1 = new ConcreteEmployee(s, "ZhangSan");
ConcreteEmployee o2 = new ConcreteEmployee(s, "LiSi");
ConcreteEmployee o3 = new ConcreteEmployee(s, "WangEr");
ConcreteEmployee o4 = new ConcreteEmployee(s, "ZhaoLiu");
//建立员工对象 张三 李四 王二 赵六并绑定水箱s
AnotherObserver o5 = new AnotherObserver();
//建立普通观察者对象 o5
s.Attach(new fillwaterDelegate(o1.fillWater));
s.Attach(new fillwaterDelegate(o2.fillWater));
s.Attach(new fillwaterDelegate(o3.fillWater));
s.Attach(new fillwaterDelegate(o4.fillWater));
s.Attach(new fillwaterDelegate(o5.Show));
//绑定事件fillwaterHandler
s.waterTankCubage = 2;//水箱初始状态2升
s.Notify();//事件开始运行
Console.WriteLine("--------------------------");
s.Detach(new fillwaterDelegate(o1.fillWater));
//把o1也就是员工张三加水行为从事件中剥离
s.waterTankCubage = 1; //水箱初始状态1升
s.Notify();//事件开始运行
}
}
12.2【栈和堆】
面试例题1：编号为123456789的火车经过如下轨道从左边入口处移到右边出口处(每车都必须且只能进临时轨道M一次，且不能再回左边入口处) [中国台湾计算机硬件公司W面试题，2008.11]
-----------------------------------------------------
987654321
-------------------\ /-----------------------------
| |
| |
|M|
| |
|_| 
按照从左向右的顺序，下面的结果不可能是______
A 123876549
B 321987654
C 321456798
D 987651234
解析：本题实际上考的是数据结构的栈。临时轨道M就是栈。
A 123挨个过去，45678入栈再出栈变成87654，9再过去；
B 123入栈再出栈变成321，456789入栈再出栈变成987654；
C 123入栈再出栈变成321，4567直接过去，89入栈再出栈变成98；
D 98765在前,则1234必须全部先进栈,98765过去后,剩下1234必须先回到左边,再通过才满足1234。但是题意要求不可再回到左边入口处，所以这个选项不可行。
答案：D
扩展知识：如果M只能容纳4列车。上面选项因该选哪个才可行？
16.2【性能测试】
面试例题1：在用户体验测试中，发现问题可能存在误测，误测包括两种：第1，问题被遗漏了；第2，不是问题却被认为是问题。我们的测试通常通过两种不同的测试方法来进行，他们依据完成不同的设计思路，但是有一些共同的：第1他们都能发现所有存在的问题；第2仍然会有百分之三的误测率；第3不会出现同一问题被对两个系统都误测的可能性。为了测试一个非常重要的系统，我们会把两个测试方法进行交叉测试，测试结果采取并集，我们可以这样推理：采用这种方法的时候是不会出现任何误测的情况。
以下哪项最恰当描述了上述推理？
A 上述推理是必然的，即如果前提为真，则结论一定真；
B 上述推理很强，但不是必然，即如果前提条件为真，则为结论提供很强的证明，但附加信息仍可削弱论证；
C 上述推理很弱，前提条件尽管与结论相关，但最多只为结论提供不充分证明；
D 推论不成立，因为他把某些必要的条件当作了充分的条件。
解析：因为从前提条件中只知道有两种误测，但并未说明两种误测能够在测试时就知道，因此当误测发生时，我们无法区分是哪种误测，因此即使使用两种测试方法进行交叉测试取并集，最多只能保证不被遗漏，但是不能解决不是问题却被认为是问题。
答案：D
第27章 智力测试
27.1【关于数字的智力问题】
面试例题3：1000瓶药水，其中至多有1瓶剧毒，现在给你10只小狗在24小时内通过小狗试药的方式找出哪瓶药有毒或者全部无毒（小狗服完药X小时后才会毒发。19<X<23）[中国著名互联网企业T面试题，2008年11月]
答案：按10进制方式给狗编号分别是1-10。按2进制方式给药水编号。按照药水第几位数字为1，给相应的狗服药。如下：
第1瓶 0000000001 第1位数字为1，给1号狗服药。
第2瓶 0000000010 第2位数字为1，给2号狗服药。
第3瓶 0000000011 第1、2位数字为1，给1、2号狗服药。
第4瓶 0000000100 第3位数字为1，给3号狗服药。
……
第99瓶 0001100011 第1、2、6、7位数字为1，给1、2、6、7号狗服药。
……
第455瓶 0111000111 第1、2、3、7、8、9位数字为1，给1、2、3、7、8、9号狗服药。
……
第1000瓶 1111101000 第4、6、7、8、9、10位数字为1，给4、6、7、8、9、10号狗服药。
最后看哪只狗毒发，则通过狗的编号得出药瓶号码。比如1、2、3、7、8、9号狗毒发，则455瓶（编号0111000111）为毒药。