通俗的来讲(仅代表个人理解),可以将数据区存储看成一个书架(栈)和一座图书馆(堆) 堆栈内存是动态的,还有静态区域(程序运行编译时内存就固定的)。书架上存放这一些值类型 、一些对象的引用、方法参数、局部变量等 。而图书馆中存放着较为复杂的、有特殊功能的、需要存储信息的对象数据类型,如:string 自定义类型等 称他们为引用类型。引用类型将在图书馆内的地址指引放在书架上,我们可以直接在书架上对值类型和引用类型的地址进行操作,当书架上没有了某个引用类型对象的地址指引,该对象则会被GC整理内存。
值类型不一定只存在在栈区,因为一些引用类型可以将值类型作为成员 一起存储在堆区。所以值类型总是存在声明它的地方。
目录
类与结构体是经常使用的综合信息(数据+行为)传递的两种方式。
类地址指引:
用于实例化对象,通过实例构造函数new出的对象属于引用类型,在创建时会被分配到托管堆内存上,而使用变量名去接的 实际上是该对象在堆内存上的地址指引。
如代码所示:
在方法中的d为局部变量 存在栈中,当当前方法执行完毕后 d将会被清掉失效,而在方法中的实例类对象将会失去地址指引,即没有任何变量在引用它,接下来该对象则会被垃圾自动回收掉。
class Program
{
static void Main(string[] args)
{
Func1();//调用
}
static void Func1()
{
//声明MyClass1类型的变量去接 相当于接收新对象在堆内存上的地址 并给他起名叫d
//d的存放位置为栈区 d为局部变量
MyClass1 d
= new MyClass1();
//在堆内存上开辟空间 存放一个类型为MyClass1的对象 并返回在堆上的地址指引
}
}
public class MyClass1
{
}
如代码所示:
将一个方法内部创建的对象返回,并进行字段存储,作为成员字段存于program中 一起存在堆区上,这样MyClass1 对象就不算丢失,只要引用一直存在,该对象不会被垃圾自动回收。
class Program
{
static MyClass1 myClass1 = null;//声明一个类型为MyClass1静态字段
static void Main(string[] args)
{
myClass1 = Func2();
//将Fun2返回的MyClass1的对象引用堆类成员进行赋值存储
}
/// <summary>
/// 创建一个MyClass1对象
/// </summary>
/// <returns>返回一个类型为MyClass1的对象</returns>
static MyClass1 Func2()
{
MyClass1 s = new MyClass1();//在方法内部创建一个对象到堆上
return s;
//并将地址指引返回出去 一般情况下返回某个对象的地址指引 称为返回一个某类型的对象
}
}
public class MyClass1
{
}
结构体:
有时程序运行中特征数据比较少 仅需要在栈区传递一些信息数据同时还有方法 用完就扔,该信息无需再重复调用,甚至无需内存记录,不需要像对象实例一样在堆与栈之间来回传递地址,可以使用结构体来进行综合信息传递。以下对于结构体的编写和注意事项不包括.net8版本。vs最近的框架对结构体机制增加了优化。
格式:访问权限修饰符 + struct +结构体名 + { 结构体主体 }
1、结构体中不允许手写无参构造;
2、如果手写了有参构造,那么 必须保证在有参构造中将所有的字段和属性赋上值;
3、无论我们是否手写构造,系统都会给我们提供无参构造;
4、结构体中的字段不允许赋初始值;
5、结构体只能继承接口。
结构体与类的区别:
1.数据类型不同: 结构体属于值类型, 创建在声明他的地方;类属于引用类型,创建在堆区,栈区留的时地址引用。
2.使用目的不同:结构体特征数据较少,为了程序运行中方便传输数据;类特征数据较多,为了存储信息方便复用。
3.结构不同:
结构体系统时钟会提供一个公共无参构造并不允许手写无参构造,若手写有参构造,在有参构造结束前必须保证所有字段或属性有值;
类在未声明构造时,系统会提供一个公开无参构造并且允许手写无参构造,若手写了有参构造函数,系统不会再提供无参构造。
4.对象特征不同:结构体只能继承接口并不可参与其他继承,并且不包含索引器;类可以继承类和多个接口;
值类型和引用类型的区别:
值类型总是存储在声明它的地方:
1.若值类型在方法中或一段执行逻辑中声明,是作为局部变量存储在栈区。当逻辑块执行完毕后由编译器释放。
2.若值类型被以类成员声明在类中,当该类被实例化时,跟随该对象存储在堆区。当该对象被回收时,由GC释放。
1.存储位置不同:
值类型总是存储在声明它的地方,栈区存储的值类型 存取快;引用类型存储总在堆区。 引用类型存取慢;
栈区 :Stack(堆栈),栈负责保存 代码的执行(或调用)路径;存放 方法的参数、 局部变量、返回值等数据;由编译器自动释放的。
堆区:Heap(托管堆):主要保存的是对象(或者说数据)的路径;存放的引用类型的 对象,由CLR内存管理机制 释放的。
2.数据传输不同:
值类型传递的时数据的值 是实际数据,收发两方的数据是分离的;
引用类型传递的是当前对象的指引,收发两方的数据都指向当前对象,修改读取指向的是同一个。
3.性能不同:
值类型访问速度比堆区快,但若频繁的生成或删除值类型会占用过多内存或性能。
引用类型需要通过地址指引才能到对象内存上访问速度和创建速度较慢,引用类型有C#提供的GC内存回收机制,但引用类型传递的是地址,性能消耗小。
4.内存释放不同:
栈的内存是 编译器自动释放的; 堆 的内存是.NET中会由GC释放;
5.基类不同:
值类型 继承自 System.ValueType;引用类型 继承了Object; 但是不管是值类型还是引用类型,最终都继承了Object。