11. 堆和栈的区别?
通常保存着我们代码执行的步骤,如在代码段1中 AddFive()方法,int pValue变量,int result变量等等。 而堆上存放的则多是对象,数据等。(译者注:忽略编 译器优化)我们可以把栈想象成一个接着一个叠放在 一起的盒子。当我们使用的时候,每次从最顶部取走 一个盒子。
栈也是如此,当一个方法(或类型)被调 用完成的时候,就从栈顶取走(called a Frame,译 注:调用帧),接着下一个。堆则不然,像是一个仓 库,储存着我们使用的各种对象等信息,跟栈不同的 是他们被调用完毕不会立即被清理掉。
GC方面:
栈保持着先进后出的原则,是一片连续的内存域,有系统自动分配和维护,产生的垃圾系统自动释放。而堆是无序的,他是一片不连续的内存域,用户自己来控制和释放,如果用户自己不释放的话,当内存达到一定的特定值时,通过垃圾回收器(GC)来回收。
存储方面:栈通常保存着我们代码执行的步骤,如方法变量等等。而堆上存放的则多是对象,数据等。我们可以把栈想象成一个接着一个叠放在一起的盒子(越高内存地址越低)。当我们使用的时候,每次从最顶部取走一个盒子,当一个方法(或类型)被调用完成的时候,就从栈顶取走接着下一个。堆则不然,像是一个仓库,储存着我们使用的各种对象等信息,跟栈不同的是他们被调用完毕不会立即被清理掉。
缓存方面:
栈使用的是一级缓存,他们通常都是被调用时处于存储空间中,调用完毕立即释放;堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
存储方面:栈(Stack)是一种先进后出的数据结构,在内存中,变量会被分配在栈上来进行操作。堆(heap)是用于为引用类型的实例(对象),分配空间的内存区域,在堆上创建一个对象,会将对象的地址传给栈上的变量(反过来叫变量指向此对象,或者变量引用此对象)—–也就是栈上的变量指向了堆上地址为XXX的实例(对象)。
12. 静态构造函数
静态构造函数既没有访问修饰符,也没有参数。
在创建第一个类实例或任何静态成员被引用时,.NET将自动调用静态构造函数来初始化类。
一个类只能有一个静态构造函数。
无参数的构造函数可以与静态构造函数共存。
最多只运行一次。
静态构造函数不可以被继承。
如果没有写静态构造函数,而类中包含带有初始值设定的静态成员,那么编译器会自动生成默认的静态构造函数。
如果静态构造函数引发异常,运行时将不会再次调用该构造函数,并且在程序运行所在的应用程序域的生存期内,类型将保持未初始化。
在类的构造函数前加上static会报什么错?为什么?
构造函数格式为public+类名如果加上 static 会报错(静态构造函数不能有访问、型的对象,静态构造函数只执行一次;
运行库创建类实例或者首次访问静态成员之前,运行库调用静态构造函数;
静态构造函数执行先于任何实例级别的构造函数;
显然也就无法使用this和 base 来调用构造函数。
一个类只能有一个静态函数,如果有静态变量,系统也会自动生成静态函数
13. C# String类型比 stringBuilder 类型的优势是什么?
如果是处理字符串的话,用string中的方法每次都需要创建一个新的字符串对象并且分配新的内存地址,而 stringBuilder 是在原来的内存里对字符串进行修改,所以在字符串处理方面还是建议用stringBuilder这样比较节约内存。但是 string 类的方法和功能仍然还是比 stringBuilder 类要强。
string类由于具有不可变性(即对一个 string 对象进行任何更改时,其实都是创建另外一个 string 类的对象),所以当需要频繁的对一个 string 类对象进行更改的时候,建议使用StringBuilder 类,StringBuilder 类的原理是首先在内存中开辟一定大小的内存空间,当对此 StringBuilder 类对象进行更改时, 如果内存空间大小不够, 会对此内存空间进行扩充,而不是重新创建一个对象,这样如果对一个字符串对象进行频繁操作的时候,不会造成过多的内存浪费,其实本质上并没有很大区别,都是用来存储和操作字符串的,唯一的区别就在于性能上。
String主要用于公共 API,通用性好、用途广泛、读取性能高、占用内存小。
StringBuilder主要用于拼接 String,修改性能好。
不过现在的编译器已经把String的 + 操作优化成 StringBuilder 了, 所以一般用String 就可以了
String是不可变的,所以天然线程同步。
StringBuilder可变,非线程同步。
14. C#函数 Func(string a, string b)用 Lambda 表达式怎么写?
(a,b) => {};
15. 虚函数实现原理
每个虚函数都会有一个与之对应的虚函数表,该虚函数表的实质是一个指针数组,存放的是每一个对象的虚函数入口地址。对于一个派生类来说,他会继承基类的虚函数表同时增加自己的虚函数入口地址,如果派生类重写了基类的虚函数的话,那么继承过来的虚函数入口地址将被派生类的重写虚函数入口地址替代。
那么在程序运行时会发生动态绑定,将父类指针绑定到实例化的对象实现多态。
16. 指针和引用的区别
引用不能为空,即不存在对空对象的引用,指针可以为空,指向空对象。
引用必须初始化,指定对哪个对象的引用,指针不需要。
引用初始化后不能改变,指针可以改变所指对象的值。
引用访问对象是直接访问,指针访问对象是间接访问。
引用的大小是所引用对象的大小,指针的大小,是指针本身大小,通常是4字节。
引用没有const,指针有const
引用和指针的+自增运算符意义不同。
引用不需要分配内存空间,指针需要。
17. C#中有哪些常用的容器类,各有什么特点。
List,HashTable,Dictionary,Stack,Queue
Stack栈:先进后出,入栈和出栈,底层泛型数组实现,入栈动态扩容2倍
Queue队列:先进先出,入队和出队,底层泛型数组实现,表头表尾指针,判空还是满通过size比较
Queue和Stack主要是用来存储临时信息的
Array数组:需要声明长度,不安全
ArrayList数组列表:
动态增加数组,不安全,实现了IList接口(表示可按照索引进行访问的非泛型集合对象),Object数组实现
List列表:
底层实现是泛型数组,特性,动态扩容,泛型安全
将泛型数据(对值类型来说就是数据本身,对引用类型来说就是引用)存储在一个泛型数组中,添加元素时若超过当前泛型数组容量,则以2倍扩容,进而实现List大小动态可变。(注:大小指容量,不是Count)
LinkList链表:
1、数组和List、ArrayList集合都有一个重大的缺陷,就是从数组的中间位置删除或插入一个元素需要付出很大的代价,其原因是数组中处于被删除元素之后的所有元素都要向数组的前端移动。
2、LinkedList(底层是由链表实现的)基于链表的数据结构,很好的解决了数组删除插入效率低的问题,且不用动态的扩充数组的长度。
3、LinkedList的优点:插入、删除元素效率比较高;缺点:访问效率比较低。
HashTable哈希表(散列表)
概念:不定长的二进制数据通过哈希函数映射到一个较短的二进制数据集,即Key通过HashFunction函数获得HashCode
装填因子:α=n/m=0.72 ,存储的数据N和空间大小M
然后通过哈希桶算法,HashCode分段,每一段都是一个桶结构,一般是HashCode直接取余。
桶结构会加剧冲突,解决冲突使用拉链法,将产生冲突的元素建立一个单链表,并将头指针地址存储至Hash表对应桶的位置。这样定位到Hash表桶的位置后可通过遍历单链表的形式来查找元素。
1、Key—Value形式存取,无序,类型Object,需要类型转换。
2、Hashtable查询速度快,而添加速度相对慢
3、Hashtable中的数据实际存储在内部的一个数据桶里(bucket结构体数组),容量固定,根据数组索引获取值。
//哈希表结构体
private struct bucket {
public Object key;//键
public Object val;//值
public int hash_col;//哈希码
}
//字典结构体
private struct Entry {
public int hashCode; // 除符号位以外的31位hashCode值, 如果该Entry没有被使用,那么为-1
public int next; // 下一个元素的下标索引,如果没有下一个就为-1
public TKey key; // 存放元素的键
public TValue value; // 存放元素的值
}
private int[] buckets; // Hash桶
private Entry[] entries; // Entry数组,存放元素
private int count; // 当前entries的index位置
private int version; // 当前版本,防止迭代过程中集合被更改
private int freeList; // 被删除Entry在entries中的下标index,这个位置是空闲的
private int freeCount; // 有多少个被删除的Entry,有多少个空闲的位置
private IEqualityComparer<TKey> comparer; // 比较器
private KeyCollection keys; // 存放Key的集合
private ValueCollection values; // 存放Value的集合
性能排序:
插入性能: LinkedList > Dictionary > HashTable > List
遍历性能:List > LinkedList > Dictionary > HashTable
删除性能: Dictionary > LinkedList > HashTable > List
18. C#中常规容器和泛型容器有什么区别,哪种效率高?
不带泛型的容器需要装箱和拆箱操作速度慢所以泛型容器效率更高数据类型更安全
19. 有哪些常见的数值类?
简单值类型:包括 整数类型、实数类型、字符类型、布尔类型
复合值类型:包括 结构类型、枚举类型
20. 泛型是什么
多个代码对 【不同数据类型】 执行 【相同指令】的情况
泛型:多个类型共享一组代码
泛型允许类型参数化,泛型类型是类型的模板
5种泛型:类、结构、接口、委托、方法
类型占位符 T 来表示泛型
泛型类不是实际的类,而是类的模板
从泛型类型创建实例
声明泛型类型》通过提供【真实类型】创建构造函数类型》从构造类型创建实例
类 泛型类型参数
性能:泛型不会强行对值类型进行装箱和拆箱,或对引用类型进行向下强制类型转换,所以性能得到提高
安全:通过知道使用泛型定义的变量的类型限制,编译器可以在一定程度上验证类型假设,所以泛型提高了程序的类型安全。