1.类型
又名数据类型,是性质相同的值的集合,并且有自己一套专门的操作运算。 内存是程序运行的区域,数据放在内存中。
(1)类型在c#中的作用
·存储此类型变量所需要的内存空间大小。比如说此时类型是int类型,那我们就会知道这个变量占用的内存大小。
·可以知道此类型的值的最小最大范围。
(2)程序的静态和动态:就是说程序未运行起来的时候是静态,这个程序运行起来的时候就是动态的。(不是我们一般说的 那种加static的静态)当我们运行一个程序的时候可以说这个程序从静态向动态进行转换。
(3)堆和栈的简介
程序运行起来之后会把内存分成2个区域,一个是栈,一个是堆。栈是给方法调用的,堆是存放对象实例的。栈很小只有2M左右,但是很快,堆可以达到几个G。当算法没写好或者程序有错误的时候,会造成栈溢出。对于堆来说不会出现溢出的问题,但是如果我们任意的在往堆上分配对象,没有释放,会造成内存的浪费,一般称为内存泄漏。
如下面的方法,展示了栈溢出的情况,一直调用BadMethod方法,一直往栈中插入值。
namespace TestClass { class Program { static void Main(string[] args) { Bad bad = new Bad(); bad.BadMethod(); Console.ReadKey(); } } class Bad { public void BadMethod() { int x = 1; this.BadMethod(); } } }
(4)c#的五大数据类型
(5)变量,内存,对象
从表面上讲,变量的用途是存储数据。
实际上,变量表示了存储位置,不并且每个变量都有一个类型,以决定什么样的值能够存入变量。
当程序运行的时候是在内存中运行的,没有运行的时候可以看做是在硬盘中,程序运行中,变量的值也是在内存中存储的。
比如定义:int x=100;计算机会x当做一个标签,这个标签对应着内存中的一个地址,100这个值就存在这个地址指向的内存中。int类型占用4个字节,从这个地址往后的4个字节的内存来保存这个变量,所以说明只有小于等于4字节的值才会放到这个变量中。
变量一共有7种:静态变量,实例变量,数组元素,值参数,引用参数,输出形参,局部变量。
变量就是以变量名所对应的内存地址为起点,以其数据类型所要求的存储空间为长度的一块内存区域。
(6)值类型与引用类型
计算机是以字节(byte)为基础单元进行数据的存储读取,并且为每个字节都准备了一个唯一的编号,我们通常所说的内存地址就是计算机科学中说的字节的编号。
1.值类型
比如:int x=10;
此时计算机系统一看是值类型,就会在内存中找到空闲的栈内存区域,并根据变量的数据类型,取得对应的字节长度一块区域,将10以二进制的方式存储到内存中。
2.引用类型
class Program { static void Main(string[] args) { Student student; student = new Student(); Console.ReadKey(); } } class Student { uint ID { get; set; } ushort Score { get; set; } }
看上面的代码,在创建student对象的时候,计算机系统一看是引用类性,会在栈内存中找到空闲的内存区域,然后默认分配4个字节的长度。
student = new Student();这一步操作则会在堆内存中找到空闲区域,创建分配一个Student实例,在堆中的这个内存中才是存储ID,Score这些信息,然后把堆上的这个内存地址保存在student这个对象中。将这个内存地址转化成二进制存放到栈内存中。
如果后续加上下列代码:
Student student2;
student2 = student;
此时会在栈内存中先默认创建一个4字节长度的内存区域,之后再将student对象在栈中存储的实例的地址信息复制到student2中,此时这个2个对象指向的就是同一个地址了。
注意:在上述代码中,类Student中的2个变量是值类型的,但是他们的值是随着实例存储在堆中的,而不是栈上的。这就是通常说的值类型的值可能在栈上存储,也可能在堆上存储。也可以记为局部变量(类中方法体内部的变量称为局部变量)是存储在栈上的。所以在方法内部的student对象的值存储在栈上,因为是在方法体内创建的一个变量,但是new Student()是创建了实例,所以是存储在堆上。而且类中的ID,Score我们称之为字段,或者成员变量,不是局部变量,所以存储在堆上。
实例构造函数的内存机理:
class Program { static void Main(string[] args) { Student student = new Student(1,"name"); Console.ReadKey(); } } class Student { int ID { get; set; } string Name { get; set; } public Student(int id,string name) { this.ID = id; this.Name = name; } }
上面代码实现了一个带参数的构造函数, Student student = new Student(1,"name");这一步会先在栈上找到一块内存区域, 然后实例化,在堆上找到一块内存区域存放类中的ID,Name。但是这次调用了带参数的构造器,值类型的id值会在直接放到最开始找到的堆内存中,对于引用类型的name参数则又会在堆上另找一块内存区域存放字符串的值,最开始的内存区域剩下的地方用来存新找的内存地址与值类型的值。
(7)拆箱与装箱
1.装箱
下面就是一个装箱的代码:都是局部变量
int x=10; object o=x;
int x=10;这一步会在栈内存中创建一个4个字节长度的内存区域,Object o;这一步会在栈上创建一个内存区域来存储对象o。当计算机发现Object所要引用的值是在栈内存中存储的数据时候,会复制这个对象在栈内存中存储的数据,然后到堆上找到一块内存空间,存储这个值,然后把这个堆地址赋给对象。在本例中,就是把堆地址赋给对象o。
2.拆箱
int y=(int)o
此时会在栈内存中存储一个对象y,然后根据对象o的存储的地址找到堆中的数据,然后赋给y。
所以拆箱装箱会损失性能
引用于:https://www.bilibili.com/video/BV13b411b7Ht?p=7