现在的位置: 首页 > 综合 > 正文

(转)面试题

2012年08月29日 ⁄ 综合 ⁄ 共 6204字 ⁄ 字号 评论关闭

> 什么是.NET?什么是CLI?什么是CLRIL是什么?JIT是什么,它是如何工作的?GC是什么,简述一下GC的工作方式?

CLI是规范; CLR是对CLI的实现; .NET是基于CLR构建的一套框架;

开发人员需要通过IL与CLR进行交流, 虽然IL本身支持一些面向对象的概念, 但是对于开发人员来讲还是过于复杂低效, 于是C#应运而生, 程序员只需编写C#代码, csc编译器会将其翻译成IL;

虽然CLR理解IL, 但是CPU只认识二进制指令, 所以CLR需要JIT的帮助, 将IL翻译成CPU指令. JIT按需工作, 当一个.NET方法即将被执行时, JIT会介入, 把该方法(IL指令) 编译成CPU指令, 并保存以供重用.

GC被用来回收当前进程中已无人使用的垃圾对象; GC会被某些事件触发(比如, 0代对象满, 内存压力大, appdomain被卸载), 随后遍历GC堆上的对象, 并通过”该对象是否被root直接/间接引用 (更进一步, “一个对象是否可以被回收”还会依赖于F-Reachable Queue 和GC Handle Table”)来判断一个对象是否需要被回收.

> 类(class)和结构(struct)的区别是什么?它们对性能有影响吗?.NET BCL里有哪些是类(结构),为什么它们不是结构(类)?在自定义类型时,您如何选择是类还是结构?

简单说来, 类是引用类型, 结构是值类型;

值类型对象直接分配在当前线程的栈上, 引用类型对象位于GC堆上, 所以值类型对象无法在多个方法中传递, 而引用类型会承担更多的任务, 比如, 用于线程同步 (Monitor.Enter(…))当一个引用类型的对象被创建时, 会需要4+4=8个byte的额外空间开销(32bit OS), 同时过度的使用引用类型对象会增加GC堆的压力, 频繁的GC对程序的性能还是有一些影响的.

BCL中的Byte类型是一个结构体, 至于为什么它不是一个类, 我觉得可能是设计者认为Byte不应该被继承, 或者8个byte的额外开销无法承受吧.

什么时候使用值类型/引用类型还是应当具体情况具体分析, 对于引用类型, 能用sealed就sealed, 在方法调用时开销会小一些.

> .NET程序运行过程中,什么是堆,什么是栈?什么情况下会在堆(栈)上分配数据?它们有性能上的区别吗?结构对象可能分配在堆上吗?什么情况下会发生,有什么需要注意的?

.NET进程被创建时就会有一个堆随之被创建, 用来保存该进程在运行中需要使用的对象/数据; 当一个线程被创建时, 会有一个栈被创建,用来保存方法调用参数, 局部变量等轻量型数据.

当一个类里面包含一个结构体类型的变量时, 该结构体类型会被分配在堆上. (不知道有什么需要注意的…)

> 泛型的作用是什么?它有什么优势?它对性能有影响吗?它在执行时的行为是什么?.NET BCL中有哪些泛型类型?举例说明平时编程中您定义的泛型类型

泛型有利于算法重用.

.NET进程地址空间中, 对象和类型是分开存放的, 当我们实例化一个泛型的时候 (比如List<int> list = new List<int>();), 会有一个新的类型对象被创建(该对象并不位于GC堆上), 当我们在使用这个实例化泛型去创建新的对象时, 才会有一个对象(GC堆上)被创建. 所以性能上会有些许的损失. 当我们使用一个值类型作为参数,去调用一个接收引用类型参数的方法是, 会有装箱发生, 这时我们可以考虑实现一个泛型, 并在运行时确定方法的参数类型.

> 异常的作用是什么?.NET BCL中有哪些常见的异常?在代码中您是如何捕获/处理异常的?在“catch (ex)”中,“throw”和“throw ex”有什么区别?您会如何设计异常的结构,什么情况下您会抛出异常?

呃, 异常可以通知我们程序出错, 比如ArgumentException, NullReferenceException…

异常的发生会导致一次stack walk, 去寻找对应的exception handler, 在这个过程中, stack trace的信息会被一层层的收集, throw ex会清空之前收集的stack trace的信息, 相当于抛出了一个新的异常, 而throw不会, 所以throw ex不利于找出问题所在.

不同的layer应该catch不同exception, 最上层处理最general的exception, 底层去处理一些detail的exception.

> List<T>T[]的区别是什么,平时你如何进行选择?Dictionary<TKey, TValue>是做什么的?.NET BCL中还有哪些常用的容器?它们分别是如何实现的(哪种数据结构)?分别是适用于哪些场景?

T[] 继承自Array, 而List<T>仅仅是对T[]的封装; 相比于T[], List<T>的size是动态变化的.

Dictionary<TKey, TValue>可以用来存储键/值对.其他的比如HashTable, SortedList等.

> 抽象类和接口有什么区别?使用时有什么需要注意的吗?如何选择是定义一个完全抽象的抽象类,还是接口?什么是接口的显式实现?为什么说它很重要?

抽象类定义了一个类及其子类是什么, 而接口更多的表现出一个类可以做什么.

当一个类实现连个不同的接口, 而这两个接口中包含一些相同签名的方法时需要用到显示实现.

> 字符串是引用类型类型还是结构类型?它和普通的引用类型相比有什么特别的地方吗?使用字符串时有什么需要注意的地方?为什么说StringBuilder比较高效?在连接多个字符串时,它无论何时都比直接相加更高效吗?

String是引用类型, 其特殊之处在于一个string是不可变的, 当我们对两个string使用连接操作时, 会生成一个新的string 对象, 而原来的两个string保持不变.

在和native代码做interop时, 对于传出参数 (char* outStr), 应当选择使用stringbuilder而非string.

StringBuilder内部维护着一个char[] 数组, 在做连接字符的操作时会动态增加其大小, 但是, 当原有的数组不够用时, StringBuilder会重新创建一个新的char[]数组, 值得注意的是原来的数组不会被抛弃, 新创建的数组只会用作存储新添加的字符.

如果说StringBuilder有”不高效”的话,应该就是在原有数组空间用尽的情况下吧.

> 如何高效地进行数组复制?二维数组数组的数组有什么区别?在使用双重循环遍历一个二维数组时,如何选择内外层的遍历顺序?

不清楚, 因为数组的元素在内存中的分布是连续的, 我能想到的方法是直接使用内存拷贝API.

二维数组是二维的, 数组的数组是一维的.

根据内存局部性原理, CPU在读取二维数组的第一个元素是, 第一行的数据也会被一起读入cache, 所以应当先遍历行,随后遍历列.

> 什么是.NET?什么是CLI?什么是CLRIL是什么?JIT是什么,它是如何工作的?GC是什么,简述一下GC的工作方式?

CLI是规范; CLR是对CLI的实现; .NET是基于CLR构建的一套框架;

开发人员需要通过IL与CLR进行交流, 虽然IL本身支持一些面向对象的概念, 但是对于开发人员来讲还是过于复杂低效, 于是C#应运而生, 程序员只需编写C#代码, csc编译器会将其翻译成IL;

虽然CLR理解IL, 但是CPU只认识二进制指令, 所以CLR需要JIT的帮助, 将IL翻译成CPU指令. JIT按需工作, 当一个.NET方法即将被执行时, JIT会介入, 把该方法(IL指令) 编译成CPU指令, 并保存以供重用.

GC被用来回收当前进程中已无人使用的垃圾对象; GC会被某些事件触发(比如, 0代对象满, 内存压力大, appdomain被卸载), 随后遍历GC堆上的对象, 并通过”该对象是否被root直接/间接引用 (更进一步, “一个对象是否可以被回收”还会依赖于F-Reachable Queue 和GC Handle Table”)来判断一个对象是否需要被回收. 具体细节还这真不是半个小时能讲完的J

> 类(class)和结构(struct)的区别是什么?它们对性能有影响吗?.NET BCL里有哪些是类(结构),为什么它们不是结构(类)?在自定义类型时,您如何选择是类还是结构?

简单说来, 类是引用类型, 结构是值类型;

值类型对象直接分配在当前线程的栈上, 引用类型对象位于GC堆上, 所以值类型对象无法在多个方法中传递, 而引用类型会承担更多的任务, 比如, 用于线程同步 (Monitor.Enter(…))当一个引用类型的对象被创建时, 会需要4+4=8个byte的额外空间开销(32bit OS), 同时过度的使用引用类型对象会增加GC堆的压力, 频繁的GC对程序的性能还是有一些影响的.

BCL中的Byte类型是一个结构体, 至于为什么它不是一个类, 我觉得可能是设计者认为Byte不应该被继承, 或者8个byte的额外开销无法承受吧.

什么时候使用值类型/引用类型还是应当具体情况具体分析, 个人倾向引用类型, 因为这样在设计的时候会少费些脑子, 不过能用sealed就sealed, 在方法调用时开销会小一些.

> .NET程序运行过程中,什么是堆,什么是栈?什么情况下会在堆(栈)上分配数据?它们有性能上的区别吗?“结构”对象可能分配在堆上吗?什么情况下会发生,有什么需要注意的吗?

.NET进程被创建时就会有一个堆随之被创建, 用来保存该进程在运行中需要使用的对象/数据; 当一个线程被创建时, 会有一个栈被创建,用来保存方法调用参数, 局部变量等轻量型数据.

当一个类里面包含一个结构体类型的变量时, 该结构体类型会被分配在堆上. (不知道有什么需要注意的…)

> 泛型的作用是什么?它有什么优势?它对性能有影响吗?它在执行时的行为是什么?.NET BCL中有哪些泛型类型?举例说明平时编程中您定义的泛型类型.

泛型有利于算法重用.

.NET进程地址空间中, 对象和类型是分开存放的, 当我们实例化一个泛型的时候 (比如List<int> list = new List<int>();), 会有一个新的类型对象被创建(该对象并不位于GC堆上), 当我们在使用这个实例化泛型去创建新的对象时, 才会有一个对象(GC堆上)被创建. 所以性能上会有些许的损失. 当我们使用一个值类型作为参数,去调用一个接收引用类型参数的方法是, 会有装箱发生, 这时我们可以考虑实现一个泛型, 并在运行时确定方法的参数类型.

> 异常的作用是什么?.NET BCL中有哪些常见的异常?在代码中您是如何捕获/处理异常的?在“catch (ex)”中,“throw”“throw ex”有什么区别?您会如何设计异常的结构,什么情况下您会抛出异常?

呃, 异常可以通知我们程序出错, 比如ArgumentException, NullReferenceException…

异常的发生会导致一次stack walk, 去寻找对应的exception handler, 在这个过程中, stack trace的信息会被一层层的收集, throw ex会清空之前收集的stack trace的信息, 相当于抛出了一个新的异常, 而throw不会, 所以throw ex不利于找出问题所在.

不同的layer应该catch不同exception, 最上层处理最general的exception, 底层去处理一些detail的exception.

> List<T>T[]的区别是什么,平时你如何进行选择?Dictionary<TKey, TValue>是做什么的?.NET BCL中还有哪些常用的容器?它们分别是如何实现的(哪种数据结构)?分别是适用于哪些场景?

T[] 继承自Array, 而List<T>仅仅是对T[]的封装; 相比于T[], List<T>的size是动态变化的.

Dictionary<TKey, TValue>可以用来存储键/值对.其他的比如HashTable, SortedList等.

> 抽象类和接口有什么区别?使用时有什么需要注意的吗?如何选择是定义一个完全抽象的抽象类,还是接口?什么是接口的显式实现?为什么说它很重要?

抽象类定义了一个类及其子类是什么, 而接口更多的表现出一个类可以做什么.

当一个类实现连个不同的接口, 而这两个接口中包含一些相同签名的方法时需要用到显示实现.

> 字符串是引用类型类型还是结构类型?它和普通的引用类型相比有什么特别的地方吗?使用字符串时有什么需要注意的地方?为什么说StringBuilder比较高效?在连接多个字符串时,它无论何时都比直接相加更高效吗?

String是引用类型, 其特殊之处在于一个string是不可变的, 当我们对两个string使用连接操作时, 会生成一个新的string 对象, 而原来的两个string保持不变.

在和native代码做interop时, 对于传出参数 (char* outStr), 应当选择使用stringbuilder而非string.

StringBuilder内部维护着一个char[] 数组, 在做连接字符的操作时会动态增加其大小, 但是, 当原有的数组不够用时, StringBuilder会重新创建一个新的char[]数组, 值得注意的是原来的数组不会被抛弃, 新创建的数组只会用作存储新添加的字符.

如果说StringBuilder有”不高效”的话,应该就是在原有数组空间用尽的情况下吧.

> 如何高效地进行数组复制?二维数组数组的数组有什么区别?在使用双重循环遍历一个二维数组时,如何选择内外层的遍历顺序?

不清楚, 因为数组的元素在内存中的分布是连续的, 我能想到的方法是直接使用内存拷贝API.

二维数组是二维的, 数组的数组是一维的.

根据内存局部性原理, CPU在读取二维数组的第一个元素是, 第一行的数据也会被一起读入cache, 所以应当先遍历行,随后遍历列.

> 什么是元编程,.NET有哪些元编程的手段和场景?什么是反射?能否举一些反射的常用场景?有人说反射性能较差,您怎么看待这个问题?有什么办法可以提高反射的性能吗

对元编程了解十分有限, .NET的CodeDom(或者使用reflection emit动态创建类型)应当是其中的一个场景.

基于元数据, 反射帮助我们在运行时动态获取程序集/类型/方法/属性等等的信息, 可以用于加载Addin.

有得必有失, 反射功能强大, 只要不是滥用, 其带来的益处远大于性能上的损失.

> 委托是什么?匿名方法是什么?在C# 3.0中,Lambda表达式是什么?扩展方法是什么?LINQ是什么?您觉得C# 3.0中还有哪些重要的特性,它们带来了什么优势?BCL中哪些类库和这些特性有关?您平时最常用哪些

委托可以认为是类型安全的函数指针, 此处省略741个字…

> 工作之外您看哪些技术相关的书、网站、社区、项目等等?您还接触哪些.NET以外的技术,能和.NET.NET中有针对性的部分做个对比吗

OneCode是个不错的项目 <http://1code.codeplex.com/>

 

http://blog.csdn.net/Sento/archive/2011/03/06/6226381.aspx

题目来源:http://blog.zhaojie.me/2011/03/my-interview-questions-for-dotnet-programmers.html

抱歉!评论已关闭.