《C#从现象到本质》读书笔记第二篇
第2章 C#类型基础(上)
类型指的是集合{类,结构,接口,枚举,委托}中的任意一个成员。任何拥有某类型的值(value)称为某类型的一个实例(instance)。
类型可以被分为值类型(结构,枚举,整型,布尔型,datetime)以及引用类型(类、接口、指针、字符串、委托、数组)。
C#支持6种类型访问修饰符。访问范围大小排序为:
1)public:没有限制。
2)protected internal:只能在所在程序集、定义的类型或派生类型进行访问。
3)internal:只能在所在程序集访问。
4)private protected:c#7.2新增的访问修饰符,等同于5和6中满足任意一个。
5)protected:只能由定义的类型或派生类型进行访问。
6)private:只能由定义的类型进行访问。
除了public和internal,其他4种修饰符不能作用于非嵌套类。
如果没有指定访问修饰符,类型的访问修饰符默认为internal,类型成员则默认为private(都是限制最大的那个)。
堆基于进程,属于进程内存空间的一部分;栈基于线程。
引用类型对象包括方法表指针和同步块索引(值类型则没有这两样东西),方法表指针指向该引用类型自己的类型对象。引用类型默认值为null。
引用类型的赋值分为深复制和浅复制。默认的情况为浅复制,浅复制只会复制地址本身,然后将这个地址赋值给新的变量,所以,新的对象和旧的对象同事指向堆上的旧对象。更改任何一个对象成员的值都会影响另一个。
深复制复制一个对象时,不仅仅把对象的引用进行赋值,还把该对象引用的值一起复制。值类型就是典型的例子。
值类型的内存分配:
1)值类型作为局部变量:普通的值类型总是分配在栈上。
2)值类型作为引用类型的成员:如果值类型为引用类型的成员,则遵从引用类型的内存分配(托管堆)和复制方式(浅复制)。
3)值类型中包含引用类型:如果一个结构体中包含了引用类型(例如结构体),则它引用类型的那部分会遵从引用类型创建的内存分配,值类型的那部分则遵从值类型创建的内存分配。
设置值类型的目的是提高程序的性能。值类型一定是密封的,不支持继承。
适用使用结构体的情况:
1)当对象的所有属性都需要在创建之初即赋值时。
2)当对象的全部属性都是值类型时。
3)当对象不需要被继承时。
值类型和引用类型在内存中的分配区别是决定其应用不同的根本原因。
按值传递的实质是传递值,不同的是这个值在值类型和引用类型的表现是不同的:
参数为值类型时,“值”为实例本身,因此传递的是实例拷贝,不会对原来的实例产生影响;
参数为引用类型时,“值”为对象引用,因此传递的是引用地址拷贝,会改变原来对象的引用指向。
值类型和引用类型的区别与联系:
它们的区别主要有:
1)所有值类型隐式派生自system.valuetype。该类确保值类型所有的成员全部分配在栈上。有三个例外:
a)结构体如果含有引用类型成员,则该成员也会牵扯到堆的分配。
b)静态类型,如果一个变量是静态的,则无论它是什么类型,都会分配在加载堆上。
c)局部变量被捕获升级为密封类。
2)引用类型的初值为null,值类型则是0.
3)引用类型,栈中会有一个变量名和变量类型,指向堆中对象实例的地址。值类型仅有栈中的变量名和类型,不包括指向实例的指针。
4)值类型不能被继承,引用类型可以。
5)值类型的生命周期是其定义域。当值类型离开其定义域后将被立即销毁。引用类型则会进入垃圾回收分代算法。
6)值类型的构造函数必须为所有成员赋值。
7)可以重写引用类型的析构函数。值类型不需要析构函数。析构函数只会被垃圾回收器调用。
8)值类型没有同步块索引,不能作为线程同步工具。
它们的联系主要有:
1)值类型和引用类型可以通过装箱和拆箱互相转化。
2)所有值类型都派生自system.valuetype,它是system.object的子类。
3)类和结构体都可以实现接口。
装箱需要比原数据更多的空间,因为它需要两个引用类型的标准配置:类型对象指针和同步块索引。
装箱就是把值类型转换为object类型或由此值类型实现的任何接口类型。
拆箱的目的是为了将值拷贝到一个值类型中,所以拆箱之后,往往伴随着一次值的复制动作。