作者:张云翯(编著)
出版社:信息技术第一出版分社
格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT
.NET程序员面试秘笈试读:
前言
本书的思路从目前软件开发的就业形势来看,.NET程序员的就业率相当不错,因为使用.NET开发项目的公司特别多,微软公司对.NET的支持也比较完善。下面的图给出了从2008年到2013年人们对ASP.NET招聘职位的关注度,从最初的30多万提高到了目前的100多万。本书的目的就是让更多学习.NET的开发者能轻松就业。本书的特色
本书的考题实例都具有较强的实践性,读者可以根据考题的具体情况,轻松完成实例的制作。本书的特点主要体现在以下几个方面。
本书的编排采用循序渐进的方式,适合初级、中级.NET 学习人员逐步掌握.NET Framework的基本原理。
本书精选大量典型的面试考题,在解题的过程中,不但分析题目所考查的知识点,还提供了解题思路及相关知识点的详细讲述。
本书在介绍.NET 技术时,采用了较简洁的例子,大多数考题的实例,读者可以快速完成编写并立即见到成效。
本书在介绍ASP.NET 技术时,以ASP.NET 的编译过程、无状态特性为起点,避免了.NET基础知识的重复学习,使读者能够快速掌握ASP.NET的程序编写。
本书有大量实际项目的开发方法提示,使读者可以轻松将知识应用到实际项目中。
本书的所有实例源代码都附在随书光盘中,方便读者使用。本书的内容安排
本书内容突出了在.NET面试中或者是.NET项目开发中,必须掌握的技能和容易忽略的内容,对.NET面试者来说,可以快速掌握面试过程中考察的知识点,减少面试准备时间,提高面试成功率。本书共分为11章180余道面试题。第1章 .NET 概念题
本章内容包含面向对象程序设计和.NET的基础概念题,由于这部分题目涉及的范围比较广,因此在很多公司的面试题中占了比较大的比重。第2章 .NET 编程语言基础题
本章内容包含C#语言各方面的题目,涉及部分容易忽略的C#基本语法,如值类型和引用类型、装箱和拆箱操作、枚举和结构等,还有.NET平台的内存管理和异常处理机制。第3章 基类、接口和泛型
本章包含基类应用(如System.DataTime、System.IO)、接口和集合、泛型方面的知识,相对前两章的内容,本章侧重实际应用,会用一些小示例来配合讲解每个面试题的解析。第4章 .NET 高级特性
本章的内容包含了.NET框架中的高级特性,主要有委托、事件、反射、线程、序列化、LINQ、匿名方法等。第5章 Windows 窗体编程
本章内容以窗体程序中控件及 GDI+相关的编程知识为主,.NET 提供了大量的控件以提升程序开发效率,读者可以通过这些面试题来重新认识控件。第6章 ADO.NET 编程
本章包括ADO.NET在数据库方面的应用题目,涉及ADO.NET连接式访问数据库和断开式访问数据库的知识,其中会涉及SQL Server 数据库。第7章 SQL查询及LINQ
本章讲述数据库的一些基本概念题,以及各种SQL查询语句相关的基础题目。本章还涉及.NET Framework 中的LINQ 技术,其相关的题目应用了LINQ 中比较基础同时也比较典型的几个知识点,其中包括LINQ to SQL 技术以及LINQ to XML 技术。第8章 ASP.NET 程序开发1
本章讲述 ASP.NET 程序开发的相关题目。ASP.NET 是.NET 重要的组成部分,所以ASP.NET的题目在面试题目中占的比例较大。本章包括一些Code-Behind技术、@Page指令等常见的ASP.NET题目。第9章 ASP.NET 程序开发2
本章的内容仍然和ASP.NET程序相关,主要分为Web.config配置文件相关题、ASP.NET数据相关题和建站技巧。本章的题目更贴近实际项目开发的知识需要和实践经验的考查,这要求面试者善于对开发经验的积累和归纳。第10章 Web Service 和远程处理
.NET 技术可以应用多种远程应用技术,其中,Web Service逐渐被企业应用大量采用。本章先以简单的实例说明Web Service 的基本概念,这些概念在面试中或多或少都有涉及,理解了基本概念后编写实际项目的难度并不大。第11章 算法和趣味题
本章讲述编程中的算法题目。算法是程序设计的核心,在笔试中出现的机会很多,面试者应做好充分的准备。对于同样一个问题可以用多种方法完成,面试应该选择最有效率的方法,这就是考官所要考查的内容,所以这种题目也充满了趣味性。本书读者群
希望进入程序开发行业的新手。
希望快速转向.NET 平台进行程序开发的开发人员。
希望从事.NET 开发岗位的开发人员。
希望进一步提高.NET 开发水平的中级开发人员。
对.NET 平台技术感兴趣的爱好者。
ASP.NET 和C#入门人员。编者第1章.NET概念题
本章内容包含面向对象程序设计和.NET的基础概念题,由于这部分题目涉及的范围比较广,因此在很多公司的面试题中占了比较大的比重。很多.NET程序员在编写代码时非常熟练,但往往缺乏对基础知识的深刻理解,从而导致面试失败。这类知识是程序设计的基础,如果不加以重视,程序编写就没有创造性,只能学一步,做一步。
面向对象编程是当前流行的编程方式,被大多数高级语言支持。.NET程序同样是基于面向对象的设计,只有深刻理解面向对象的编程理念,才可以开发出结构良好的、更易维护的.NET程序。
说明:本书采用C#语言编写.NET程序示例。面试题1 简述面向对象的程序设计思想【考点】面向对象程序设计思想概念的理解,面向对象设计的应用范围,用C#实现面向对象设计方法。【出现频率】★★★★☆【解答】
面向对象是程序开发的一种机制,其特征为封装、继承、多态及抽象。以面向对象的方式编写程序时,需要将复杂的项目抽象为多个对象互相协作的模型;然后编写类型的结构,声明或实现类型的成员,即描述对象的特征和行为。在项目中编写不同的类型完成不同的功能,最后在程序中通过类的实例处理实际的问题。
说明:此处类型不仅仅指类,还可能包括接口、结构等。【分析】
面向对象编程简称为OOP,其“对象”的含义类似于生活中的“物体”。相对于以前的编程方式,面向对象编程更注重于人的思维方式,可极大地提高程序的开发效率。面向对象编程将程序看作多个互相分离的功能模块,各个模块之间分工合作,并且有着极低的耦合度。面向对象编程中最重要的概念是类(即class),类是描述各个功能模块的模板,而程序中工作的模块实体被称为对象(即object)。
类和对象的概念好比动物学的分类,猫科动物就是一个类,老虎也是一个类,并且属于猫科动物类,动物园中某只老虎的实体则是一个对象。老虎类拥有猫科动物类的所有特征和行为,但有自己独特的特征和行为。而某只老虎符合老虎类特征描述和行为定义,可能还有部分独特的特征。所以类可以继承另一个类,如老虎类继承了猫科动物类。类是产生对象的一个模板,对象拥有类的一切特征和行为。关于类和对象的基本关系如图1.1所示。图1.1 类和对象示意图
面向对象编程的重点是类的设计,面试者应能熟练地编写简单的类并创建对象,展示基本的OOP语法。以图1.1为例,编写图中相应的类,并通过虎类创建一只体重为100kg、长度为 200cm 的老虎对象。新建一个.cs 文件,并命名为 OopTest.cs,编写代码如程序1.1所示。程序1.1 老虎对象的创建:OopTest.cs
01 using System;
02 class OopTest
03 {
04 static void Main(String[] args)
05 {
06 Console.WriteLine("请输入老虎对象的体重和长度(逗号分隔的整数):");
07 string input= Console.ReadLine();
08 int pos = input.IndexOf(",");
09 string w = input.Substring(0, pos);
10 string l = input.Substring(pos + 1);
11 Tiger chinatiger = new Tiger(w, l);
12 Console.WriteLine("老虎已经创建完成!");
13 Console.WriteLine("这只老虎的重量是:"+chinatiger.weight+"kg");
14 Console.WriteLine("这只老虎的长度是:" + chinatiger.length + "cm");
15 Console.WriteLine("老虎的特征是:" + Tiger.msg);
16 Console.WriteLine("老虎一般生存在:" + Tiger.habitat );
17 Console.WriteLine("老虎像猫吗?" + Tiger.cat());
18 }
19 }
20 class Mammal //哺乳动物类名称
21 {
22 protected static bool viviparous = true; //有胎生的特征
23 protected static bool Feeding = true; //有喂养的特征
24 }
25 class Felid : Mammal //猫科动物类名称(继承于哺乳动物类)
26 {
27 protected static bool catlike = true; //类似猫的特征
28 protected static bool sensitivity = true; //有敏感的特征
29 }
30 class Tiger : Felid //虎类名称(继承于猫科动物类)
31 {
32 //有各种特征
33 internal static string msg = "凶猛、会游泳、会爬树,有漂亮的花纹,被称为“兽中之王”。";
34 internal static string habitat = "森林"; //栖息地在森林
35 internal string weight; //体重
36 internal string length; //长度
37 internal Tiger(string w, string l) //构造函数,直接给体重和长度赋值
38 {
39 this.weight = w;
40 this.length = l;
41 }
42 internal static bool cat() //通过静态方法获取继承的属性
43 {
44 return Tiger.catlike;
45 }
46 }
在命令行下编译OopTest.cs后,执行OopTest程序,其效果如图1.2所示。图1.2 OopTest的执行结果
当用户首先输入了“50,100”,程序创建了一只老虎的对象,并访问了部分数据字段和方法。
说明:下文所有当前章的程序示例将在当前章编号的目录下创建并运行,最后进行归档,例如第1章每个示例文件归档到ch01目录下,而第2章每个示例文件归档到ch02目录下,依此类推。面试题2 用代码描述类和对象的区别【考点】理解类和对象的关系,在实际应用中类和对象的作用。【出现频率】★★★★★【解答】
类(即class)指一类事物,对象(即object)指属于这一类事物的实体。类定义了对象的相关数据和方法,类通过构造函数生成对象,对象实现了类的定义,且拥有具体的数据。在ch01目录下新建一个程序文件,并命名为ClassObj.cs,编写代码如程序1.2所示。程序1.2 类和对象:ClassObj.cs
01 using System;
02 class ClassObj
03 {
04 static void Main(String[] args)
05 {
06 Console.WriteLine("请输入动物对象的类别(pig,dog,cat):");
07 string animal= Console.ReadLine(); //读取用户输入到animal变量
08 Console.WriteLine("请输入动物对象的品种(如“波斯”):");
09 string tp = Console.ReadLine(); //读取用户输入到tp变量
10 Console.WriteLine("请输入动物对象的体重:");
11 string w = Console.ReadLine(); //读取用户输入到w变量
12 switch (animal.ToLowerInvariant()) //判断animal变量值
13 {
14 case "pig":
15 Pig pg = new Pig(tp, w); //根据tp和w的值创建Pig类的对象
16 Console.Write("\n你创建了一只" + Pig.name + "!");
17 Console.WriteLine("它的品种是:" + pg.type + "!");
18 Console.WriteLine("这只" + Pig.name + "的重量是:" + pg.weight + "kg");
19 Console.WriteLine(Pig.name + "的特征是:" + Pig.msg );
20 break;
21 case "dog":
22 Dog dg = new Dog(tp, w); 根据tp和w的值创建// Dog类的对象
23 Console.Write("\n你创建了一只" + Dog.name + "!");
24 Console.WriteLine("它的品种是:" + dg.type + "!");
25 Console.WriteLine("这只" + Dog.name + "的重量是:" + dg.weight + "kg");
26 Console.WriteLine(Dog.name + "的特征是:" + Dog.msg );
27 break;
28 default:
29 Cat ct = new Cat(tp, w); //根据tp和w的值创建Cat类的对象
30 Console.Write("\n你创建了一只" + Cat.name + "!");
31 Console.WriteLine("它的品种是:" + ct.type + "!");
32 Console.WriteLine("这只" + Cat.name + "的重量是:" + ct.weight + "kg");
33 Console.WriteLine(Cat.name + "的特征是:" + Cat.msg );
34 break;
35 }
36
37 }
38 }
39 class Pig //猪类名称
40 {
41 internal static string name = "小猪";
42 internal static string msg = 肥胖、迟钝";" //有各种特征
43 internal string type; //品种
44 internal string weight; //体重
45 internal Pig(string tp, string w) //构造函数, 接给品种和体重赋值直
46 {
47 this.type = tp;
48 this.weight = w;
49 }
50 }
51 class Dog //狗类名称
52 {
53 internal static string name = "小狗";
54 internal static string msg = "属于犬科动物,是人类的好朋友"; //有各种特征
55 internal string type; //品种
56 internal string weight; //体重
57 internal Dog(string tp, string w) //构造函数,直接给品种和体重赋值
58 {
59 this.type = tp;
60 this.weight = w;
61 }
62 }
63 class Cat //猫类名称
64 {
65 internal static string name = "小猫";
66 internal static string msg = "安静、敏感"; //有各种特征
67 internal string type; //品种
68 internal string weight; //体重
69 internal Cat(string tp, string w) //构造函数,直接给品种和体重赋值
70 {
71 this.type = tp;
72 this.weight = w;
73 }
74 }
例子比较简单,但可以充分说明类和对象的关系,3个动物类的描述如图1.3所示。图1.3 类和对象程序的执行结果
图1.3为3个类的定义,在主程序中用户输入动物的类别、品种和体重数据后,即根据以上类为模板创建了一只小动物对象,并输出其数据。在命令行下编译ClassObj.cs后,执行ClassObj程序,其效果如图1.4所示。【分析】
简单来说,类是用于描述对象的特征、状态和行为的模板,是抽象的概念。例如虎类可以用于描述所有老虎个体的共同特征及行为,但是虎类不能指定某一只老虎的个体。而对象指拥有具体特征、状态和行为的实体,例如某只老虎就是虎类的一个对象,它遵从类所描述的一切特征和行为。图1.4 类和对象程序的执行结果面试题3 描述private、protected、internal和public修饰符的作用【考点】理解访问权限存在的意义,各种访问权限的作用,在代码中灵活应用访问权限。【出现频率】★★★☆☆【解答】
1.private修饰符
private 修饰符用于设置类或类成员的访问权限仅为所属类的内部,private 也被称为私有修饰符。某些时候需要访问私有类成员时,可通过get和set访问器读取或修改。本例通过对类的私有成员及私有类的访问,展示private修饰符的保护作用。在ch01目录下新建一个程序文件,并命名为ModPrivate.cs,编写代码如程序1.3所示。程序1.3 private修饰符示例:ModPrivate.cs
01 using System;
02
03 class ModPrivate
04 {
05 static void Main(String[] args)
06 {
07 Dog dg = new Dog("可卡");
08 Console.WriteLine("一只小狗创建成功!");
09 Console.WriteLine("这只"+dg.name + "的品种是:" + dg.type);
10 //由于参数类型为字符串的构造函数是私有的,这里不能直接创建实例对象
11 //Tiger tg = new Tiger("华南虎");
12 //由于Tiger类所嵌套的ZooTiger类是私有类,所以无法直接访问
13 //Tiger.ZooTiger tz = new Tiger.ZooTiger();
14 Tiger tg = new Tiger(true); //参数类型为布尔型的构造函数可创建Tiger类的对象
15 Console.WriteLine("\n一只老虎创建成功!");
16 Console.WriteLine("这只" + tg.name + "的品种是华南虎吗?" + tg.ischinatiger);
17
18 }
19 }
20 class Dog //狗类名称
21 {
22 internal string name = "小狗";
23 private string _type; //品种
24 internal Dog(string tp) //构造函数,直接给品种赋值
25 {
26 this._type = tp;
27 }
28 internal string type //type变量,get访问器获取私有成员_type的值
29 {
30 get { return this._type; }
31 }
32 }
33 class Tiger //虎类名称
34 {
35 internal string name = "老虎";
36 private string _type; //品种
37 private bool _ischinatiger; //是否为华南虎
38 private Tiger(string tp) //构造函数,直接给品种赋值
39 {
40 this._type = tp;
41 }
42 internal Tiger(bool chinatiger) //构造函数,直接给_ischinatiger赋布尔型值
43 {
44 this._ischinatiger = chinatiger;
45 }
46 internal string ischinatiger //ischinatiger变量,get访问器获取返回值
47 {
48 get
49 {
50 //由于同属于Tiger类的内部,所以可创建ZooTiger私有类的对象
51 ZooTiger zt = new ZooTiger();
52 //返回字符串,内容为私有成员_ischinatiger的值和ZooTiger的对象的name值
53 return this._ischinatiger + "(" + zt.name + ")";
54 }
55 }
56 private class ZooTiger
57 {
58 internal string name;
59 internal ZooTiger()
60 {
61 this.name = "动物园的老虎";
62 }
63 }
64 }
2.protected修饰符
protected 修饰符用于设置类或类成员的访问权限仅为所属类及子类的内部。本例通过对类的私有成员及私有类的访问,展示private修饰符的保护作用。在ch01目录下新建一个程序文件,并命名为ModProtected.cs,编写代码如程序1.4所示。程序1.4 protected 修饰符示例:ModProtected.cs
01 using System;
02
03 class ModProtected
04 {
05 static void Main(String[] args)
06 {
07 Console.WriteLine("请输入所需创建老虎对象的品种(如东北虎、华南虎、孟加拉虎等):");
08 string input = Console.ReadLine(); //读取用户输入,并存储于input变量
09 string nm, tp;
10 if (input != "华南虎") //如果input变量存储的不是"华南虎"字符串
11 {
12 Tiger tg = new Tiger(input); //创建Tiger类的对象,并传递input变量的值
13 nm = tg.name; //将tg对象的name属性赋值给nm变量
14 tp = tg.type; //将tg对象的type属性赋值给tp变量
15
16 }
17 else
18 {
19 ChinaTiger tg = new ChinaTiger(); //创建ChinaTiger类的对象
20 //将tg对象的name属性(继承于父类中声明为protected的属性)赋值给nm变量
21 nm = tg.name;
22
23 //将tg对象的type属性(继承于父类中声明为protected的属性)赋值给tp变量
24 tp = tg.type;
25 }
26 Console.WriteLine("\n一只{0}创建成功!",nm);
27 Console.WriteLine("这只{0}的品种是:{1}" ,nm,tp);
28 }
29 }
30 class Tiger //虎类名称
31 {
32 protected string _name = "老虎";
33 protected string _type; //品种
34 internal Tiger() //无参数构造函数
35 {
36 }
37 internal Tiger(string t) //构造函数,直接给品种赋值
38 {
39 this._type = t;
40 }
41 internal string name //name变量,get访问器获取返回值
42 {
43 get
44 {
45 return this._name; //返回字符串,内容为私有成员_name的值
46 }
47 }
48 internal string type //type变量,get和set访问器获取返回值或写入值
49 {
50 get
51 {
52 return this._type; //返回字符串,内容为私有成员_type的值
53 }
54 set
55 {
56 this._type = value; //为私有成员_type赋值
57 }
58 }
59 }
60 class ChinaTiger : Tiger //华南虎类名称
61 {
62 internal ChinaTiger() //构造函数,直接给品种赋值
63 {
64 this._type = "华南虎"; //直接赋值"华南虎"字符串给父类中继承的_type属性
65 }
66 }
3.internal修饰符
internal修饰符修饰的类或类成员的访问权限为同一程序集内部,C#默认的类访问修饰符即为internal。前面两个示例中,需要供类外部代码访问的成员都使用了internal修饰符,因为这些类都处于同一程序集中。
4.public修饰符
public修饰符则为公共访问权限,对代码的访问没有任何限制。大多数情况下须谨慎使用public修饰符,因为滥用将影响类的封装性,并且带来安全隐患。
以下为代码的运行结果:(1)在命令行下编译ModPrivate.cs后,执行ModPrivate程序,其效果如图1.5所示。
从本例代码中可知,ZooTiger类无法在类的外部直接访问,所有的私有成员只能在类的内部访问,本例采用了get访问器访问了小狗和老虎的品种,并创建了ZooTiger私有类的对象。有的读者可能会迷惑,如果同时使用get和set访问器private,修饰符意义何在?其实很多程序中确实有这样的做法,这样做向类的外部屏蔽了私有成员的实现方法,同时也隐藏了私有成员的实际名称,有利于封装性。例如本例g, et访问器中有两步操作,而外界无法获知。图1.5 private修饰符示例(2)在命令行下编译 ModProtected.cs 后,执行 ModProtected 程序,其效果如图1.6所示。图1.6 protected修饰符示例
本例接收用户的输入,当输入值为“华南虎”时,创建 ChinaTiger 类的对象,并通过构造函数赋值“华南虎”字符串给_type字段。_type字段声明中使用了protected修饰符,所以在Tiger类的子类(ChinaTiger类)中可被直接访问。
注意:即使派生类和基类在不同程序集中,派生类仍可访问基类的 protected修饰符成员。读者必须清楚的一点是,派生类继承了所有基类的成员,只是无法直接访问基类的private修饰符成员,但可访问protected修饰符成员。【分析】
面向对象编程的特征之一就是封装性,而类就是封装性的典型体现。在生活中,人们不需要知道电器的内部构造,但是能很容易地使用电器,这就是封装性。在项目中需要编写很多功能类,在程序运行时只需要使用类所提供的功能,大部分类内部的代码实现需要封装,拒绝外部访问。这样大大增加了类的内部代码安全性和稳定性,同时多个功能类之间也减少了互相干扰的可能。访问权限修饰符即可将类及类的成员划分多种安全级别,根据不同需要设置访问权限。
internal和public访问修饰符是需要谨慎选择的,多数情况下应该尽量使用internal访问修饰符。
还有一种访问修饰符,即protected internal 修饰符,可在子类中或同一程序集内被访问。如果要声明成员只能被同一程序集内的派生类(子类)访问,则应首先设置所属类为internal,成员设置为protected。面试题4 举例说明属性、get和set访问器的使用【考点】对属性(Property)的理解,C#中get和set访问器的编写方法,理解自动实现的属性。【出现频率】★★☆☆☆【解答】
本例通过属性操作类中声明的私有字段_username,请注意在Name属性的get和set访问器中的逻辑操作。本例还有一个自动实现的属性,可读取用户输入的数据。在ch01目录下新建一个程序文件,并命名为GetSet.cs,编写代码如程序1.5所示。程序1.5 属性示例:GetSet.cs
01 using System;
02
03 class GetSet
04 {
05 static void Main(String[] args)
06 {
07 Console.Write("请输入用户名:");
08 Detail dt = new Detail(); //创建Detail的对象
09 dt.Name = Console.ReadLine();
10 Console.WriteLine("Name属性写入完成!");
11 Console.WriteLine("\n您的用户名为{0}(读取Name属性)", dt.Name);
12 Console.Write("\n请输入密码:");
13 dt.Password = Console.ReadLine();
14 Console.WriteLine("Password属性写入完成!");
15 Console.WriteLine("\n您的密码为{0}(读取Password属性)", dt.Password);
16 }
17 }
18 class Detail
19 {
20 private string _username;
21 internal string Name
22 {
23 get
24 {
25 //从_username值的第6位开始截取并返回(字符串索引值从0开始)
26 return _username.Substring(5);
27 }
28 set
29 {
30 _username = "user-" + value; //在传入值前面加上"user-"
31 }
32 }
33 internal string Password //Password属性内部为自动属性实现
34 {
35 get;
36 set;
37 }
38 }
在命令行下编译GetSet.cs后,执行GetSet程序,其效果如图1.7所示。图1.7 属性示例
本例中,首先创建Detail类的对象dt,将用户第1次输入数据赋值给dt对象的Name属性。在Name属性的set访问器中,用户输入的字符串前面拼接了“user-”字符串,然后赋值给_username 字段。当读取 Name 属性时, get 访问器将_username 字段的值执行Substring(5)方法,将“user-”字符串去掉后返回。从整个过程来看,用户无法知道属性中数据经过了什么处理及数据最终存储在何处。而用户输入第2次数据,其值被写入Password属性,自动属性实现可将值写入匿名后备字段,读取时亦可直接返回其值。
说明:本例仅展示了get和set访问器在属性中的使用,在索引器中使用方法是一样的。【分析】
在前面的例子中使用了get和set访问器,通过访问器可以很方便地访问私有成员。其对外部暴露的部分可以为属性或索引器,属性比较常用。类体中的属性(Property)在使用时和一般的类成员没有区别,只是在内部实现的方法通过get和set访问器完成,这样更灵活、更隐蔽、更安全。其编写格式如以下代码所示:
访问修饰符 数据类型 属性名称
{
get
{
[访问修饰符2] 相关数据操作;
}
[访问修饰符3] set
{
和value关键字有关的数据操作;
}
}
当直接读取属性名称时,将会使用 get 访问器,执行“相关数据操作”的内容,相当于执行一个返回值为“数据类型”的方法。当直接赋值给属性名称时,被赋予的新值将替换隐式参数value,执行相关的数据操作。从代码中可知,get或set可添加访问修饰符,不过必须在get和set同时存在的情况下,且不能同时添加。当没有set访问器时,代表该属性为只读。
最常用的数据操作是读取和写入类中的私有字段(被称为后备字段),如果 get 和 set访问器中不需要逻辑,仅仅通过属性完成赋值和写入值的功能,可以使用自动实现的属性。自动实现的属性可以提供比较简洁的格式,并且编译器将自动创建一个匿名后备字段(在类体中没有声明),其编写方法如以下代码所示:
访问修饰符 数据类型 属性名称{get;set}
访问修饰符 数据类型 属性名称{get;private set}
第1种自动实现的属性可直接读取和写入,使用者并不知道数据读取或写入了哪个字段(匿名后备字段)。第2种自动实现的属性为只读,无法写入。
注意:属性提供了比较灵活的数据访问方法,读者编写代码时注意显式声明的set访问修饰符必须比属性修饰符更严格。get 或 set 没有显式访问修饰符时,其默认访问限制和属性一致。面试题5 描述sealed修饰符的使用【考点】sealed修饰符的意义,密封类和抽象类的关系,sealed修饰符的用法。【出现频率】★★★☆☆【解答】
在ch01目录下新建一个程序文件,并命名为Sealed.cs,编写代码如程序1.6所示。程序1.6 sealed修饰符示例:Sealed.cs
01 using System;
02
03 class Sealed
04 {
05 static void Main(String[] args)
06 {
07 Console.WriteLine("请输入电脑CPU的名称:");
08 string c = Console.ReadLine();
09 Computer lenovo = new Computer(c); //创建电脑对象
10 Console.WriteLine("\n{0}所采用的CPU为{1}", lenovo.name, lenovo.cpu);
11 Phone nokia = new Phone(); //创建手机对象
12 nokia.display(); //调用重写后的密封方法display()
13 }
14 }
15 class Product
16 {
17 internal string name;
18 internal virtual void display()
19 {
20 Console.WriteLine("这是产品类的方法!");
21 }
22 }
23 sealed class Computer:Product //声明电脑类,继承于Product类
24 {
25 internal string cpu ;
26 internal Computer(string c)
27 {
28 this.cpu = c;
29 this.name = "电脑";
30 }
31 }
32 /*
33 class NoteBook : Computer
34 {
35 }
36 *声明本类将导致错误,因为Computer类为密封类,无法被继承
37 */
38 class Phone : Product //声明手机类,继承于Product类
39 {
40 internal sealed override void display()
41 {
42 Console.WriteLine("\n这是手机类的方法!");
43 }
44 }
45 class Phone3G : Phone //声明手机类,继承于Product类
46 {
47 /*internal override void display()
48 {
49 Console.WriteLine("\n这是3G手机类的方法!");
50 }
51 *声明此方法将导致错误,由于 display方法在其父类Phone中为密封方法,所以无法再重写
52 */
53 }
在命令行下编译Sealed.cs后,执行Sealed程序,其效果如图1.8所示。图1.8 sealed修饰符示例
从本例代码中可知,密封类一般情况下的使用方法和其他类一样,只是无法被继承。代码中Product类的虚方法display方法被Phone类重写,而被重写的display方法前面也加了sealed修饰符。在这种情况下,密封的display方法无法被所属类的子类继续重写,如本例代码中的Phone3G类,无法重写display方法。
说明:密封类可用于单一功能的实现,并且防止被意外地继承,产生非预期的效果,这也是封装性的体现。【分析】
sealed修饰符用于修饰类、实例方法和属性。sealed用于修饰类时,该类无法被继承,所以该类也被称为密封类。而abstract类(抽象类)必须被继承才有意义,所以,sealed修饰符和abstract修饰符是互相排斥的,无法共存。密封方法会重写基类的方法,sealed用于修饰实例被重写的虚方法或虚属性时,表示该方法或属性无法再被重写。
注意:sealed修饰实例方法或属性时,必须和override一起使用。面试题6 请简述静态类和静态类成员【考点】静态类和静态类成员的理解,static在应用中的特殊性。【出现频率】★★☆☆☆【解答】
在ch01目录下新建一个程序文件,并命名为Static.cs,编写代码如程序1.7所示。程序1.7 静态类及静态类成员示例:Static.cs
01 using System;
02
03 class Static
04 {
05 static void Main()
06 {
07 Console.Write("请输入电脑的CPU和内存规格,用英文逗号分隔:");
08 string input = Console.ReadLine(); //获取用户输入值并赋值给input变量
09 int pos = input.IndexOf(","); //获取input字符串中英文逗号的索引
10 //根据索引获取逗号前面部分的字符串并赋值给PC类的静态字段cpu
11 PC.cpu = input.Substring(0, pos);
12 //根据索引获取逗号后面部分的字符串并赋值给PC类的静态字段memory
13 PC.memory = input.Substring(pos + 1);
14 Console.Write("请输入电脑的单价和数量,用英文逗号分隔:");
15 string input2 = Console.ReadLine();
16 int pos2 = input2.IndexOf(",");
17 int p = Int32.Parse(input2.Substring(0, pos2));
18 int n = Int32.Parse(input2.Substring(pos2 + 1));
19 PC ibm = new PC(p, n); //将p和n值转换为int类型后传递给构造函数
20 Console.WriteLine("\n(1)你选择电脑的CPU是{0},内存是{1},总价是{2}元", PC.cpu,PC.memory, ibm.count());
21 CpuMsg.getmsg(); //调用静态类的静态方法
22 }
23 }
24 class PC
25 {
26 internal static string cpu="";
27 internal static string memory="";
28 private static int price;
29 private static int num;
30 internal PC(int p, int n) //构造函数接收2个参数并为2私有字段赋值
31 {
32 price = p;
33 num = n;
34 }
35 internal int count() //用于计算总价格的方法,返回值为int类型
36 {
37 return price * num;
38 }
39 }
40 static class CpuMsg
41 {
42 private static string _name="CPU";
43 private static string _comp = "Intel";
44 internal static void getmsg()
45 {
46 Console.WriteLine("(2)静态类包含的CPU名称为:{0};生产厂家为:{1}",_name,_comp);
47 }
48 }
在命令行下编译Static.cs后,执行Static程序,其效果如图1.9所示。图1.9 静态类及静态类成员示例
本例的Main方法中,首先将用户第1次输入的值通过逗号分隔为2个字符串,再分别给PC类的赋值静态字段,即cpu和memory;然后将用户的第2次输入也分隔为2个字符串,并转换为整数类型赋值给p和n。通过传递参数p和n给PC的构造函数创建了PC类的对象ibm,并进行了初始化。在用户输入值后的第1行访问了PC类的2个静态字段,并通过实例方法计算了总价。在第2行直接调用了静态类CpuMsg的静态方法getmsg(),静态类CpuMsg完全不需要实例化,可以很方便地直接在程序中使用。在.NET的类库中有很多类似的静态类,可以在程序中直接使用其方法,例如Math类。【分析】
static是比较特殊的修饰符,它所修饰的类或类成员被称为静态类或静态类成员。
当类中某些成员不需要创建实例实现,则可将其声明为静态类成员。静态成员在访问类名而非对象名,同样,“this”关键字也无法访问静态成员时直接引用。这些成员可用作该类的多个对象共享的数据,因为静态类成员不依赖某个对象。声明静态类成员如以下代码所示:
访问修饰符 static 数据类型 类成员;
当类中没有和对象实例相关的成员时,即类体中只有静态成员,可声明该类为静态类。静态类无法用new 创建对象,所以并不能编写构造函数,并且该类是密封类(即无法被继承)。静态类的声明方法如以下代码所示:
访问修饰符 static class 类名称
{
静态类成员1;
静态类成员2;
静态类成员3;
...
}
必须注意的是,类中的常数声明和类型声明默认为静态,如类体中声明 1 个类,这个类默认为static,即无法被所属类的对象访问。
注意:声明静态类时,必须保证其内含成员全部为静态成员。面试题7 构造函数有什么作用【考点】各种形态构造函数的理解,派生类的构造函数,构造函数的重载。【出现频率】★★★★☆【解答】
构造函数用于创建类的实例,并对实例进行初始化操作,通过不同的参数传递,可进行不同的实例初始化操作。本例通过多种不同形式的构造函数创建实例,并输出初始化的结果。在 ch01 目录下新建一个程序文件,并命名为 Constructor.cs,编写代码如程序1.8所示。程序1.8 构造函数示例:Constructor.cs
01 using System;
02
03 class Constructor
04 {
05 static void Main()
06 {
07 Console.Write("请输入篮球比赛的选手人数:");
08 int inputA = Int32.Parse(Console.ReadLine()); //将用户输入值转换为int类型(这里没有作异常处理)
09 Console.Write("请输入篮球比赛的MVP:");
10 string inputB = Console.ReadLine();
11 Basketball bb = new Basketball(); //用Basketball类的默认构造函数创建实例bb
12 bb.getmsg(); //实例bb调用getmsg方法
13 Basketball bbb = new Basketball(inputA, inputB); //用Basketball类带2个参数的构造函数创建bbb
14 bbb.getmsg(); //实例bbb调用getmsg方法
15 Football fb = new Football(); //用Football类的默认构造函数创建实例fb
16 fb.getmsg(); //实例fb调用getmsg方法
17 Console.WriteLine("\n本次游泳比赛的冠军是{0}队", Swim.champ); //直接访问Swim类的静态字段
18 //Shoot sh = new Shoot();此处代码将会被编译器报错,因为其默认构造函数为私有的
19 }
20 }
21 class Basketball
22 {
23 private int _playernum;
24 private string _mvp;
25 internal Basketball()
26 {
27 }
28 internal Basketball(int n, string m)
29 {
30 _playernum = n;
31 _mvp = m;
32 }
33 internal void getmsg()
34 {
35 Console.WriteLine("\n这场篮球比赛的选手有{0}个,最有价值球员是{1}!", _playernum,_mvp);
36 }
37 }
38 class Football
39 {
40 private string _star = "Henry";
41 internal void getmsg()
42 {
43 Console.WriteLine("\n这场足球比赛的明星是{0}!", _star);
44 }
45 }
46 class Swim
47 {
48 internal static string champ;
49 static Swim() //静态构造函数,用于初始化静态成员
50 {
51 champ = "中国";
52 }
53 }
54 class Shoot
55 {
56 internal static string champ=null;
57 private Shoot() //私有构造函数,无法在类外部创建实例
58 {
59 }
60 }
在命令行下编译Constructor.cs后,执行Constructor程序,其效果如图1.10所示。图1.10 构造函数示例【分析】
前面的所有示例中都使用了构造函数,因为构造函数用于创建类的实例(对象)。在类中声明构造函数可对新实例(对象)进行初始化的操作,其编写方法如以下代码所示:
class 类名称
{
访问修饰符 类名称()
{
试读结束[说明:试读内容隐藏了图片]