面试题 复盘
面试是一个很好的学习进步的机会,平时我们总是处于一种安逸的环境看不到自己的不足,面试的过程会把我们的缺点不断的暴露,让你必须不断的求索。
一:
CLI:通用语言基础结构(Common Language Infrastructure,CLI)是CLR的一个子集,也就是.NET中最终对编译成MSIL代码的应用程序的运行环境进行管理的那一部分。在 CLR结构图中CLI位于下半部分,主要包括类加载器(Class Loader)、实时编译器(IL To Native Compilers)和一个运行时环境的垃圾收集器(Garbage Collector)。CLI是.Net和CLR的灵魂,CLI为IL代码提供运行的环境,你可以将使用任何语言编写的代码通过其特定的编译器转换为 MSIL代码之后运行其上,甚至还可以自己写MSIL代码在CLI上面运行。
CLR:公共语言运行时 (Commen Language Runtime),是一个多语言运行环境,支持众多的数据类型和语言特性,负责资源管理(内存分配和垃圾收集),保证了应用和底层之间必要的分离。
IL: 是.Net平台的衍生语言,.NET 平台上面的各种高级语言的编译器会将各自的文字表达方式转换成IL。这其中包含了.Net 平台上面的各种元素,例如 泛型 类 接口 模块 等等 而且IL 本身并不知道自己是由那种高级语言编译转换过来的。
JIT: JIT(Just In Time简称JIT)是.Net边运行边编译的一种机制。 开发人员需要通过IL和CLR进行交流,IL本身支持一些面向对象的概念,但是太过复杂和低效,C# 代码编译器会将C#代码编译成为IL,CLR理解IL语言但是CPU只能识别二进制指令,所以JIT会帮助CLR将IL语言编译成CPU指令。 当.Net 方法被执行时,JIT同步工作
GC: 垃圾回收管理 应用程序的内存分配和释放 运行库都是从托管推为新NEW的对象分配内存,垃圾回收器优化引擎根据正在进行的分配情况确定执行回收的最佳时间。 当垃圾回收器执行回收是,它会检查托管堆中不再有呗应用程序使用的对象并执行必要的操作来回收他们占的内存资源。
二:类和结构的区别 对性能有什么影响,几种常见的结构体
类是引用类型 引用类型在堆上分配地址, 堆栈的执行效率要比堆高,可是堆的资源有限不适合吃力复杂的逻辑对象
结构是值类型,值类型都是在栈上分配地址,值类型将一个变量引用赋值给另外一个变量的时候,会复制该结构,因此一个副本的修改不会影响另外一条数据
.Net BCL 中的结构 :System.Boolean Byte Char Decimal Double Int32 堆栈的空间有限,创建类要比创建结构好一点,大多数情况这些类型存储的都是一些数据,所以结构是最佳的选择
类 : String Object Delegate 接口 等等 包含了大量的逻辑对象,表现抽象
三 :堆 栈
栈 通常保存我们代码执行的步骤,而堆存放的多事对象数据等, 栈内存无需我们管理也不受GC管理,当栈顶元素使用完毕后立马释放,而堆存储的是各种对象的信息,调用完毕后不会被立即清楚,所以需要GC
值类型一般分配在栈上边 引用类型一般分配在堆上,栈的效率要高于堆。
结构也有可能分配在堆上面,当在类中定义一个结构类型是,该结构就被分配到堆上
四:泛型
泛型的作用是为了促进代码的重用,尤其是算法的重用
优势:可重用性、类型安全、性能上边避免了Object强制转换和值类型的装箱、减少了内存的消耗
.Net BCL 中泛型有 List<T> 通过索引访问强类型,Dictionary<T> 键值对的集合 Quenu<T> 队列 Stack<T>栈 购物车可以用Dictionary 查询结果可以用List<T>
五:用扩展方法为C#中的string类型增加一个字符转换为数组的方法 和按照字节截取String字符串长度的方法
下边是按字节长度截取字符串的方法注册为String类的扩展方法
public static class StringExt { public static String bSubstring(this string s, int length) { byte[] bytes = System.Text.Encoding.Unicode.GetBytes(s); int n = 0; // 表示当前的字节数 int i = 0; // 要截取的字节数 for (; i < bytes.GetLength(0) && n < length; i++) { // 偶数位置,如0、2、4等,为UCS2编码中两个字节的第一个字节 if (i % 2 == 0) { n++; // 在UCS2第一个字节时n加1 } else { // 当UCS2编码的第二个字节大于0时,该UCS2字符为汉字,一个汉字算两个字节 if (bytes[i] > 0) { n++; } } } // 如果i为奇数时,处理成偶数 if (i % 2 == 1) { // 该UCS2字符是汉字时,去掉这个截一半的汉字 if (bytes[i] > 0) i = i - 1; // 该UCS2字符是字母或数字,则保留该字符 else i = i + 1; } return System.Text.Encoding.Unicode.GetString(bytes, 0, i); } }
如果最后要截取奇数个字符(以字节为单位),并且当最后一个字符是字母或数字,则保留该字符,如果是汉字,说明这个汉字被截了一半,则去掉这个汉字。
可以使用下面的代码来截取字符串:
String s = "a加b等于c,如果a等1、b等于2,那么c等3"; String subStr = s.bSubstring(6); // substr的值是"a加b等"
六:数组 链表 hash的区别
程序中存放数据最常见的数据结构就是数组和链表,
区别:数组是将数据连续存放的,而链表在内存中不是顺序存储的,是通过元素中的指2针联系到一起的。
数组是定长的不能适应数据的动态添加,而链表是动态分配存储空间
静态数组是从栈中分配空间,链表从堆中分配空间
在使用时 查询是数组能够通过下标快速的定位到需要查找的元素,而链表则需要从开始一直查找到需要的元素的位置,所以数组的查询效率要高于链表。在进行增加和删除操作时,数组都需要移动大量的元素来腾出内存空间,而链表只需要改变元素中的指针就可实现
而Hash也就是散列则兼备了快速查询和快速实现增加和删除的优势,输入数据通过hash函数能够获得一个Key值,输入的数据将会存储到数组中下标为key的数组单元中去。在实现的过程中 不同的数据可能获得相同的 哈希key值,这时候就产生了hash冲突,而解决hash冲突有两种方式,一个是建立公共溢出区,讲所有产生冲突的数据放到公共溢出区。另外一种方式就是 挂链式也叫拉链式,它的思想就是在产生冲突的hash地址指向一个链表,将具有相同key值的数据放到链表中。
如何实现hash动态增加空间的效果,这个和装填因子有密切的关系。 装填因子=填入表中的数据个数/散列表的长度. 当装填因子达到一定的值时,我们讲数组增加一定的内存空间,同时reHash.
七:C#中的Invoke和BeginInvoke
Control类实现了ISynchronizeInvoke接口,提供了Invoke和BeginInvoke方法来提供让其它线程更新GUI界面控件的机制
都需要一个委托对象作为参数,委托类似于回调函数地址,通过这两个方法就可以把需要调用的函数地址推送给界面程序
Invoke 和 BeginInvoke 是为了解决多线程更新界面显示的问题,做法是将工作线程中涉及更新界面的代码封装成一个方法,用过Invoke 和 BeginInvoke 去调用,两者的区别是Invoke 会导致工作线程的等待,而BeginInvoke 则不会
八:委托是什么 事件是不是一种委托
委托是一种类型安全的函数指针,将方法作为参数传递 当程序必须调用一个方法来执行某个操作,但编译器不知道该方法的时候可以使用委
托。
事件是一种特殊的委托, 事件的定义
public delegate void EventHandler(object sender, EventArgs e);
常见的各种控件的Click事件的定义: public event EventHandler Click;
PageLoad里:Button1.Click+=new EventHandler(Button1_Click);
定义Button1的事件 protected Button1_Click(object sender,EventArgs e){}
九:你对IOC的认识 和用过哪些 IOC框架
IOC:Inversion of Control 控制反转
DI:依赖注入 是一种解耦方式 实现IOC的思想 DI通常有三种 构造器注入 属性设置注入 接口注入
十:C#常量表达方式ReadOnly和Const的区别
C#的常量分为两种 编译时常量(Const)和运行时常量(ReadOnly)
readOnly 程序运行时赋值 也就是在声明初始化挥着构造器初始化时赋值,赋值完成后就无法再更改,也称只读变量
const为编译时常量,编译时对常量就行解析,并将多有的常量引用替换为相应值
十一:数据库有几种索引类型,索引的原理和常用索引的实现方式
数据库索引是数据库管理系统中的一个排序的数据结构,以协助快速查询和更新数据库中数据
在经常需要数据搜索的列上加索引,能够加快搜索的速度。经常给WHERE子句上的列增加索引,
对于不经常使用的列不要添加索引,因为有无索引对查询的速度没有太大的影响,
同时索引会占用物理存储空间,对于只有很少数组的列也不需要添加索引,如性别列,在查询结果中结果集中的数据表占据了数据表中的很大比例,增加索引并不会明显提高查询速度。
同时对于数据类型为 text bit image 的数据类型字段不应该增加索引,这些字段要么数据两太大,要么取值太少。
当修改性能远大于检索性能的时候也不应该建立索引, 这是因为修改性能和检索性能是相互矛盾的, 修改性能在修改数据的同时 还要修改数据库的索引。
按照数据库的功能,可以在数据库中设计三种索引,唯一索引 主键索引和聚集索引
唯一索引不允许其中任何两行具有相同的值
主键索引 在数据库关系表中创建主键会默认创建主键索引 主键索引是索引的特定类型,该索引要求主键中的每个值都唯一
聚集索引 中表中行的物理顺序和键值的索引顺序相同,一张表中只能有一个聚集索引
十二 什么是NOSQL NOSQL的优势是什么
NOSQL 即 NOT Only SQL
NOSQl种类很多,NoSQL被我们用得最多的当数key-value存储,当然还有其他的文档型的、列存储、图型数据库、xml数据库等。在NoSQL概念提出之前,这些数据库就被用于各种系统当中,但是却很少用于web互联网应用。比如cdb、qdbm、bdb数据库。
NOSQL去掉关系型数据库特性,数据库之间无关系这样就非常容易扩展,也在无形之间在架构层面带来了可扩展的能力,NOSQL还具有非常强的读写能力,这得益于它的无关系性,数据库的结构简单,NOSQL无需事先为要储存的数据建立字段,随时可以自定义数据格式,而在传统关系型数据中,数据库字段的增删字段尤其是增加字段是一件很麻烦的事情。
高可用
NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如Cassandra,HBase模型,通过复制模型也能实现高可用。
附录 常见的NOSQL数据库
十四:适用迭代器 yield关键字
迭代器是一个连续的集合,出现多个 yield return 其实就是将多个yield return 元素按照出现的顺序存储在迭代器的集合中。
/// <summary> /// 迭代器方法 /// </summary> static IEnumerable Documents(List<string> docs) { foreach (string s in docs) { yield return s; } }
上边实现迭代器的方法,调用迭代器
十五:lock
当我们使用线程的时候,效率最高的方式当然是异步,即各个线程同时运行,其间不相互依赖和等待。但当不同的线程都需要访问某个资源的时候,就需要同步机制了,也就是说当对同一个资源进行读写的时候,我们要使该资源在同一时刻只能被一个线程操作,以确保每个操作都是有效即时的,也即保证其操作的原子性。lock是C#中最常用的同步方式,格式为lock(objectA){codeB} 。
lock不能锁定值类型,lock的参数如果是值类型的话势必会发生装箱操作,这样每次lock的都是一个新的对象。通常最好避免public类型或者不受控制的对象实例,如果该是咧可以被公开访问
十六:WebService WCF WebApi
WebService 和 WCF 都是基于SOAP协议的,数据格式都是XML,并且都不是开源的,不同之处在于WebService只支持Http协议且只能部署在IIS上,WCF相对来讲配置更加的繁琐但是能够支持TCP HTTP HTTPS Named ...... 并且可以部署在应用程序中 、IIS、Windows服务中。
WebApi是一个轻量级的框架,对移动端的支持比较好。是一个开源的基于Rest-Full的,它继承了HTTP的全部特点,同时支持MVC特性,如路由 控制器 Action 模型绑定 控制反转(IOC) 依赖注入(DI) 单元测试 这样使程序更加的健壮,它可以配置在应用程序和IIS中。