值类型赋值复制值本身,引用类型赋值复制对象地址;int、struct、bool等为值类型,string、class、list等为引用类型;装箱拆箱有性能代价;判断类型应依据是否继承valuetype,而非语法表象。

值类型变量赋值时复制的是值本身
给另一个变量赋值时,int、struct、bool 这类值类型会把原始数据完整拷贝一份。两个变量互不影响,改其中一个,另一个完全没感觉。
常见错误现象:以为 Point p1 = new Point(1, 2); Point p2 = p1; 后修改 p2.X 会影响 p1.X —— 实际不会,因为 Point 是 struct,属于值类型。
- 所有内置数值类型(
int、double、decimal等)默认是值类型 - 自定义
struct也是值类型,哪怕它包含引用类型字段(字段本身被复制,但字段指向的对象地址还是共享的) - 值类型实例通常分配在栈上(方法内声明时),但也可能出现在堆上(如作为 class 的字段、或被装箱后)
引用类型变量赋值时复制的是对象地址
string、List<t></t>、class 实例这类引用类型,赋值操作只复制堆中对象的内存地址。两个变量指向同一块内存,改一个,另一个立刻可见变化。
使用场景:需要共享状态、避免大对象反复拷贝时,引用类型更高效;但也要小心意外修改——比如把一个 List<string></string> 传给方法,方法里 Add 了新元素,调用方看到的就是已修改的列表。
千博购物系统.Net能够适合不同类型商品,为您提供了一个完整的在线开店解决方案。千博购物系统.Net除了拥有一般网上商店系统所具有的所有功能,还拥有着其它网店系统没有的许多超强功能。千博购物系统.Net适合中小企业和个人快速构建个性化的网上商店。强劲、安全、稳定、易用、免费是它的主要特性。系统由C#及Access/MS SQL开发,是B/S(浏览器/服务器)结构Asp.Net程序。多种独创的技术使
-
string虽是引用类型,但不可变(immutable),每次“修改”实际是创建新对象,所以表现像值类型 -
class、interface、delegate、array都是引用类型 - 引用类型变量本身(即那个地址)存储在栈上,而它指向的对象实体在托管堆上
装箱(boxing)和拆箱(unboxing)是隐式转换,但有性能代价
把值类型转成 object 或接口类型时发生装箱,会在堆上分配新对象并复制值;反过来从 object 转回具体值类型叫拆箱,需类型匹配,否则抛 InvalidCastException。
容易踩的坑:int i = 42; object o = i; int j = (int)o; 看似简单,但如果写成 long j = (long)o; 就会运行时报错,因为拆箱只能拆回原类型或其 Nullable 形式。
- 装箱是隐式操作,拆箱必须显式强制转换
- 频繁装箱/拆箱(比如循环中往
ArrayList加int)会显著影响性能,GC 压力也会上升 - .NET Core 2.0+ 中
Span<t></t>和Memory<t></t>等类型可绕过装箱处理部分场景
判断一个类型是值类型还是引用类型不能只看 var 或声明语法
var x = 5; 推导出 int,是值类型;var y = new StringBuilder(); 推导出 StringBuilder,是引用类型。但真正决定类型的,是它的定义:是否继承自 System.ValueType(值类型)还是直接继承自 System.Object(引用类型)。
实操建议:用 typeof(T).IsValueType 在运行时判断,或看文档/源码中该类型是否用 struct 定义。别被 var 或 new 关键字误导——new 也能用于 struct(只是调用构造函数,并不分配堆内存)。
Console.WriteLine(typeof(int).IsValueType); // True Console.WriteLine(typeof(string).IsValueType); // False Console.WriteLine(typeof(DateTime).IsValueType); // True(DateTime 是 struct)值类型和引用类型的根本差异不在“存在位置”,而在“赋值行为”和“内存管理语义”。很多 bug 来自误以为 struct 字段修改会影响外部副本,或以为 class 实例赋值后彼此隔离。动手前先确认类型定义,比猜更可靠。







