C#&.NET高级面试题
1. DateTime.Parse(myString); 这段代码有什么问题?
A:区域信息即CultureInfo没有指定。如果不指定的话,它将采用默认的机器级的设置(见:控制面板->区域和语言选项)并使用这个设置来决定这个字符串即myString怎样被解释。所以如果你传入“5/2/2005”且你的区域设置为En-US,则它会被解释为May 2nd 2005,但是如果你的区域设置为Hindi-India,则它会被解释为5th Feb 2005!
参考下面的代码示例:
string sDate = "5/2/2005"; //本意是2005年5月2号
DateTime[] dt = new DateTime[3];
CultureInfo[] cf = new CultureInfo[3];
cf[0] = new CultureInfo("en-US", true); //指定为en-US,字符串将被解释为“MM/DD/YYYY”
dt[0] = DateTime.Parse(sDate, cf[0], DateTimeStyles.AllowWhiteSpaces);
dt[0] = dt[0].AddMonths(1); //另日期变为2005年6月2日
Console.WriteLine(dt[0].ToString(cf[0]));
cf[1] = new CultureInfo("hi-IN", true); //这是印度格式,字符串被解释为“DD/MM/YYYY”
dt[1] = DateTime.Parse(sDate, cf[1], DateTimeStyles.AllowWhiteSpaces);
dt[1] = dt[1].AddMonths(1); //让它变成2005年3月5日
Console.WriteLine(dt[1].ToString(cf[1]));
好了,这解决所有问题了吗?没有!
如果时间放在一个文本框中 – 有些人输入了“2005/02/05” – 我不知道我应该怎样解释这个输入呢! “DateTime.ParseExact” 要求你必须告诉计算机怎样处理输入的日期字符串它才可以处理。
//抛出FormatException
cf[2] = new CultureInfo("hi-IN", true);
dt[2] = DateTime.ParseExact(sDate, new string[] { "d" }, cf[2], DateTimeStyles.AllowWhiteSpaces); //抛出Format Exception
dt[2] = dt[2].AddMonths(1);
Console.WriteLine(dt[2].ToString(cf[2]));
第二个参数指定了输入字符串的格式 – “d”意思是短日期 – MM/dd/yyyy – 查看.net文档中的DateTimeFormatInfo类中全部的格式。
上面的代码会抛出一个FormatException,因为你没有遵守规则。
好了,现在我们几乎完成了,如果想让一个字符串表达式被接受怎么办呢?
使用一个自定义表达式。如下:
sDate = "2005/05/02";
dt[2] = DateTime.ParseExact(sDate, new string[] { "%yyyy/MM/dd" }, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces);
dt[2] = dt[2].AddMonths(1);
Console.WriteLine(dt[2].ToString(cf[2]));
这样就可以接受了
2. PDB是什么东西?在调试时它应该放在哪里?
A:以下是摘自MSDN一段说明A program database file (extension .pdb) is a binary file that contains type and symbolic debugging information gathered over the course of compiling and linking the project. A PDB file is created when you compile a C/C++ program with /ZI or /Zi or a Visual Basic, Visual C#, or JScript program with the /debug option. Object files contain references into the .pdb file for debugging information. For more information on pdb files, see PDB Files (C++). A DIA application can use the following general steps to obtain details about the various symbols, objects, and data elements within an executable image.
翻译后(部分):程序数据库文件(扩展名:pdb)是一个二进制文件包含了在编译和链接项目过程中收集的类型与符号调试信息。PDB文件在你使用/ZI或/Zi参数编译一个C/C++程序或使用/debug参数编译一个Visual Basic, Visual C#或Jscript程序时生成。目标文件含有到.pdb文件的引用以查找调试信息。
下面是查询此文件内容的方法:
u Acquire a data source by creating an IDiaDataSource interface.
u Call IDiaDataSource::loadDataFromPdb or IDiaDataSource::loadDataForExe to load the debugging information.
u Call IDiaDataSource::openSession to open an IDiaSession to gain access to the debugging information.
u Use the methods in IDiaSession to query for the symbols in the data source.
u Use the IDiaEnum* interfaces to enumerate and scan through the symbols or other elements of debug information.
调试时,PDB文件应该与待调试文件放在同一目录下。
3. 什么是圈复杂度(Cyclomatic Complexity)?为什么它很重要?
A:圈复杂度一种代码复杂度的衡量标准,中文名称叫做圈复杂度。在软件测试的概念里,圈复杂度“用来衡量一个模块判定结构的复杂程度,数量上表现为独立现行路径条数,即合理的预防错误所需测试的最少路径条数,圈复杂度大说明程序代码可能质量低且难于测试和维护,根据经验,程序的可能错误和高的圈复杂度有着很大关系”。
4. 写一个标准的lock(),在访问变量的前后创建临界区,要有“双重检查”。
A: 这即是Jeffrey Richter在其经典著作CLR Via C#中提到的双检锁(double-check locking)技巧。如下是原书代码:
public sealed class Singleton
{
private static Object s_lock = new Object();
private static Singleton s_value;
//私有构造器组织这个类之外的任何代码创建实例
private Singleton() { }
// 下述共有,静态属性返回单实例对象
public static Singleton Value
{
get
{
// 检查是否已被创建
if (s_value == null)
{
// 如果没有,则创建
lock (s_lock)
{
// 检查有没有另一个进程创建了它
if (s_value == null)
{
// 现在可以创建对象了
s_value = new Singleton();
}
}
}
return s_value;
}
}
}
5. 什么叫FullTrust?放入CAC的assembly是否是FullTrust的?
FullTrust权限基本上允许跳过所有的CAS验证,持有该权限的程序集能够访问所有的标准权限。GAC中的程序集是具有FullTrust权限的。详细信息参见此文章。
6. 代码加上需要安全权限的特性有什么好处?
A: 有可能会通过具有元权限程序集或permview.exe工具访问这些attribute及其参数。有可能在程序集级别使用其中的一些attribute。可以通过反射访问设置信息。最关键一点使程序集开始执行时而不是在执行期间声明需要权限,这样可以确保用户不会在程序开始工作后遇到障碍。
7. gacutil /l | find /i “Corillian”这句命令的作用是什么?
A: gacutil全称.NET Global Assembly Cache Utility。参数/l [ <assembly_name> ]的作用:列出通过 <assembly_name> 筛选出的全局程序集缓存。find命令作用:在文件中搜索字符串,其参数/i作用是搜索字符串时忽略大小写。所以这条命令的意思就是在所有的全局程序缓存中搜索名称包含字符串”Corillian”的程序集。
8. sn -t foo.dll这句命令的作用?
A: sn是.NET Framework 强名称实用工具,其中参数-T[p] <assembly>作用是显示 <assembly> 的公钥的标记(如果使用了-Tp,则还同时显示公钥本身)。此命令显示了foo.dll的公钥。如果foo.dll非强名程序集产生如下提示:未能将密钥转换为标记 -- 程序集“(null)”的公钥无效。
9. DCOM需要防火墙打开哪些端口?端口135是干嘛用的?
A: DCOM动态的选择1024~65535之间的网络端口。此外,DCOM要用135端口实现一些重要的功能。DCOM是使用RPC进行通讯的。利用RPC功能进行通信时,就会向对方电脑的135端口询问可以使用那个端口进行通讯。这样,对方的电脑就会告知可以使用的端口号,实际的通讯将使用这个端口来进行。
135端口起的是动态的决定实际的RPC通讯使用的端口映射功能,主要用于使用RPC(Remote Procedure Call,远程过程调用)协议并提供DCOM(分布式组件对象模型)服务。
10. 对比OOP和SOA,它们的目的分别是什么?
面向对象主要用于对具体的事物进行抽象建模,而SOA的主要目的是定义部署和管理服务的方式,采用SOA的架构可以提高可重用性,降低总成本,提高快速修改与演化IT系统的能力。
11. XmlSerializer是如何工作的?使用这个类的进程需要什么ACL权限?
XmlSerializer提供了方法,使你能够将自己的对象序列化和反序列化为XML,同时在这个过程中提供一定的控制。这个过程由一些attribute来控制。以下是代码示例:
序列化:
Theater theater = … ;
XmlSerializer xs = new XmlSerializer( typeof ( Theater ) );
FileStream fs = new FileStream( args[0], FileMode.Create );
xs.Serialize( fs, theater );
反序列化:
XmlSerializer xs = new XmlSerializer( typeof ( Theater ) );
FileStream fs = new FileStream( args[0], FileMode.Open );
Theater theater = (Theater)xs.Deserialize( fs );
控制这个过程:
[XmlRoot( "theater" )] -- 序列化xml的根节点
[XmlElement( "name" )] -- 序列化为xml的元素及元素名称
[XmlAttribute( "minutes" )] -- 序列化为xml中的特性及其名称
[XmlElement( "showing", DataType="time" )] -- 序列化为xml的元素及其类型
ACL权限上需要执行程序集的用户拥有对C:\WINDOWS\Temp\的访问权限
12. 为什么不提倡catch(Exception)?
Exception类包含许多子类,程序执行的时候要将每一个类都搜索一遍以找到符合的异常类,这样是蛮消耗资源的,影响程序运行效率。另外,只捕捉特定环境,特定条件下的异常,并进行处理。不应该捕捉所有异常,因为有些异常是我们所无法预料到的,比如,内存溢出或其他错误,这种情况下,不应该让系统以一种未知状态继续运行。
13. Debug.Write和Trace.Write有什么不同?何时应该使用哪一个?
Debug类提供一组帮助调试代码的方法和属性。Trace类提供一组帮助跟踪代码执行的方法和属性,通俗的说就是为在不打断程序的调试或跟踪下,用来记录程序执行的过程。
Debug只在debug状态下会输出,Trace在Release下也会输出,在Release下Debug的内容会消失。
14. Debug Build和Release Build的区别,是否会有明显的速度变化?请说明理由。
首先以一个表格说明问题:
项目 |
Debug |
Release |
条件编译常数 |
Debug;Trace |
Trace |
优化代码 |
False |
True |
输出路径 |
bin\Debug |
bin\Release |
生成调试信息 |
True |
False |
Debug模式下生成的程序集为调试版本,未经优化;在bin\debug\目录中有两个文件,除了要生成的.exe或.dll文件外,还有个.pdb文件,这个.pdb文件中就记录了代码中的断点等调试信息;Release模式下不包含调试信息,并对代码进行了优化,\bin\release\目录下只有一个.exe或.dll文件。在项目文件夹下除了bin外,还有个obj目录。编译是分模块编译的,每个模块的编译结果就保存在了obj目录下。最后会合并为一个exe或者dll文件保存到bin之中。因为每次编译都是增量编译,也就是只重新编译改变了的模块,所以这个obj的目录的作用就是保存这些小块的编译结果,加快编译速度。
15. JIT是以assembly为单位发生还是以方法为单位发生?这对于工作区有何影响?
JIT编译是以方法为单位完成的,当用到相应的方法才把其编译执行,并将编译结果暂存当下一次用到时就不用再编译。可以一定程度上提高效率。
16. 对比抽象基类和接口的使用
如果要让自定义类包含基类的实现,应该选用抽象基类,接口多做契约使用。.NET中类只允许单继承,但接口允许多继承。
17. a.Equals(b)和a==b相同吗?
对于值类型的比较两者效果相同(值类型ValueType重写了Equals方法)。对于除string以外的引用类型==比较对象是否为同一引用,对于string,==比较字符串的内容。而Equals方法对所有引用对象都是比较其是否为同一对象的引用。
很多自定义类都会重写Equals所以不要只注意默认实现。
18. 在对象比较中,对象一致和对象相等分别是指什么?
对象一致指的是两个对象是否指向托管堆中同一个目标,其比较对象的类型与值两个方面。对象相等指的是两个对象中的数据成员是否相等。前者是一种浅比较,后者是一种深比较。
19. 在.NET中如何实现深拷贝(deep copy)?
实现深拷贝的程序结构基本如下:
示例代码:
//实现ICloneable接口
public class Copy:ICloneable
{
//实现ICloneable接口的Clone()方法
public override object Clone()
{
//在此实现深拷贝
}
}
实现深拷贝的代码中,新建一个对象,然后将对象的成员逐个赋值给新对象的成员。最后将新对象返回。
20. 请解释一下ICloneable
当用户需要实现除MemberwiseClone之外的拷贝方法时(如深拷贝),用户可以实现ICloneable接口,并重写Clone()方法。在Clone()方法中编写自己的实现。
21. 什么叫装箱。
将值类型对象放到托管堆中需要一个装箱操作。需要装箱的情况有很多,比如将值类型数据存入一个数组(Array类)时,值类型会被装箱然后存入托管堆。
22. string是值类型还是引用类型?
string这个引用类型是非常特殊一个引用类型。
它有两点特殊的地方。第一点对象分配的特殊。例如,有如下语句:
string str1 = "abcd";
string str2 = "abcd";
那么.net在分配string类型的时候,先查看当前string类型列表是否有相同的,如果有的话,直接返回其的引用,否则重新分配。
第二点对象引用操作的特殊,即不同于真正意义上的引用操作。例如如下语句:
string str1 = "abcd";
string str2 = str1;
str2 = "efgh";// str1 is still "abcd" here
当对于一个新的string类型是原有对象引用的时候,这点和一般的引用类型一样,但是当新的对象发生变化的时候,要重新分配一个新的地方,然后修改对象指向。
因此对于string操作的时候,尤其发生变化的时候,会显得比较慢,因为其牵扯到内存地址的变化。(所以对于数据量比较大的字符操作时候,使用StringBuilder来说效率会提升很高。)
23. XmlSerializer使用的针对属性的模式有什么好处?解决了什么问题?
这里的属性指attribute。可以将不需要序列化的数据标记为[XmlIgnore],只序列化有用的数据,而不是序列化整个对象。可以省去没有必要序列化的数据序列化工作,提升序列化时的性能。使用attribute还可以指导序列化的过程,以自定义生成xml文档的格式。(11题对XmlSerializer有介绍)
24. 为什么不应该在.NET中使用out参数?它究竟好不好?
当想要一个函数产生多个输出时,可以使用out参数返回函数的输出值。这应该是out参数最大的作用。out参数的缺陷在于,它允许一个未初始化变量就在函数中作为out参数使用。这样并不能保证在访问一个作为out参数的变量时,它已经被初始化过,容易产生错误的结果。(个人观点)
25. 特性能够放到某个方法的参数上?如果可以,这有什么用?
可以,作用可以对参数有进一步限定,比如输入参数为int类型,可以通过允许AttributeTargets=ParameterInfo的Attribute自定义实现来限定输入参数的大小,比如当输入参数小于100的时候便抱错。
对方法的参数设置Attribute的例子:
[AttributeUsage(AttributeTargets.Parameter)]
public class ParameterAtt : Attribute
{
public int Min = 100;
}
public class AttributeTest
{
public void TestMethod([ParameterAtt(Min = 100)] int par1)
{
ParameterInfo para =
MethodInfo.GetCurrentMethod().GetParameters()[0];
ParameterAtt att = ParameterAtt.GetCustomAttribute
(para, typeof(ParameterAtt)) as ParameterAtt;
if (att.Min > par1)
{
throw new Exception("要求para1最小为" + att.Min);
}
}
}