C# 引用类型、值类型
c# 类型 引用
2023-09-14 09:02:11 时间
CLR支持两种类型:引用类型和值类型,它们的区别是在内存分配方式上的差异:引用类型是从托管堆上分配的;值类型是在线程栈上分配的。而CLR的垃圾回收是针对托管堆的,因此值类型不受垃圾回收器的控制。
在FCL中,所有称为“结构”(struct)的类型都是值类型,所有称为“类”(class)的类型都是引用类型。所有的Struct都直接派生自抽象类System.ValueType,
CLR支持两种类型:引用类型和值类型,它们的区别是在内存分配方式上的差异:引用类型是从托管堆上分配的;值类型是在线程栈上分配的。而CLR的垃圾回收是针对托管堆的,因此值类型不受垃圾回收器的控制。 在FCL中,所有称为“结构”(struct)的类型都是值类型,所有称为“类”(class)的类型都是引用类型。所有的Struct都直接派生自抽象类System.ValueType,而System.ValueType直接从System.Object派生。所有的枚举都直接从System.Enum派生,而后者又派生自System.ValueType,所以枚举也是值类型。由于CLR的单继承规则,所以我们在定义值类型时,不能指定基类型,但可以实现接口。同时从下图生成的IL也可以看出,值类型是隐式密封的(sealed),也就是说也不能从值类型派生。
虽然引用类型与值类型实质只是内存分配上的差异,但这种差异会导致两种类型在行为表现上有着明显不同,比如下面的例子:
图1
图2
虽然值类型实例不需要垃圾回收,但由于值类型在传递时,传递的是内容本身,所以并不适合将所一些实例较大的类型定义为值类型。实现上除非满足以下所有条件,否则不应该将一个类型声明为值类型。
在托管堆中分配好内存,分配的内存量=值类型的各个字段所需的内存量+所有堆上对象都有的两个额外成员(类型对象指针和同步块索引)所需的内存量。 值类型的字段复制到新分配的内存。 返回对象的地址。 拆箱仅是获取一个指针的过程,该指针指向包含在一个对象中的原始值类型(数据字段)。虽然拆箱比装箱代价低,但实际在拆箱之后往往紧接着就是赋值操作(内存复制)。显然装箱和拆箱/复制会对应用程序的速度与内存消耗上产生不利影响,所以应该了解到这一点,并尽量避免装箱和拆箱操作。那么什么时候会发生装箱和拆箱,最直观的方法就是看生成的IL代码(IL对应指令是分别是box与unbox),比如下面的例子:
示例中ArrayList的Add方法参数是Object类型,也就是说一个引用类型(在堆上分配的内存),当我们传递int类型时,这里便会将int实例装箱,以返回一个堆上的地址。在将array[0]强制转型为int时,由于值类型int的对象是在线程栈上分配的,所以这里拆箱并紧接着发生赋值(内存复制)操作。同时为了对比,我加了引用类型的reference,可以看出引用类型是不会发生装箱与拆箱的。
那么如何避免(或减少)装箱与拆箱:
定义一个方法如果可接收引用类型或值类型时,尽量不要将参数定义为object,可以考虑通过重载定义多个版本或定义泛型方法。 原文地址
再聊一次值类型和引用类型 这是一个托管的值类型,表示array的一个片断, 在用做websocket接收数据的载体时我发现每次值传递后, 这个ArraySegment byte 貌似发生了变化。这勾起了我的探究欲。
第183天:引用类型和值类型 一、内存分配 1、数值型内存分配 2 var num1; //这个时候不进行内存分配 3 var num3=9;//分配内存 4 var num4=num3;//会不会分配 6 // 这里到底分配还是不分配内存?? 7 // 答案 --分配内存 彼此拥有独立的内存空间,互不影响 8 console.
c#值类型与引用类型区别 值类型对象的两种表示方式:未装箱和已装箱,引用类型总是处于已装箱 值类型从System.ValueType派生。该类型提供了与System.Object相同方法,但System.ValueType重写了Equals方法, 能在两个对象的自断值完全匹配的前提下返回true。
在FCL中,所有称为“结构”(struct)的类型都是值类型,所有称为“类”(class)的类型都是引用类型。所有的Struct都直接派生自抽象类System.ValueType,
CLR支持两种类型:引用类型和值类型,它们的区别是在内存分配方式上的差异:引用类型是从托管堆上分配的;值类型是在线程栈上分配的。而CLR的垃圾回收是针对托管堆的,因此值类型不受垃圾回收器的控制。 在FCL中,所有称为“结构”(struct)的类型都是值类型,所有称为“类”(class)的类型都是引用类型。所有的Struct都直接派生自抽象类System.ValueType,而System.ValueType直接从System.Object派生。所有的枚举都直接从System.Enum派生,而后者又派生自System.ValueType,所以枚举也是值类型。由于CLR的单继承规则,所以我们在定义值类型时,不能指定基类型,但可以实现接口。同时从下图生成的IL也可以看出,值类型是隐式密封的(sealed),也就是说也不能从值类型派生。
![C 引用类型、值类型 - Complaint Free Wolrd - Complaint Free Wolrd](http://images.cnitblog.com/blog/279191/201306/22152634-395ff16306604d1eaf4f3bc40f2780db.png)
struct ValType { public int x;} class RefType { public int x;} class Program static void Main(string[] args) ValType v1 = new ValType(); //在栈上分配内存 RefType r1 = new RefType(); //在堆上分配内存 v1.x = 2; r1.x = 2; //执行到这里,内存结构请见图1 Console.WriteLine(v1.x); //2 Console.WriteLine(r1.x); //2 ValType v2 = v1; //在栈上分配内存(v2),并把v1栈的内容复制到v2 RefType r2 = r1; //把r1的堆地址复制给r2 v2.x = 5; //只改变v2栈的内容 r2.x = 5; //由于r2和r1都引用同一个堆上的对象,改变r2也会改变r1 //执行到这里,内存结构请见图2 Console.WriteLine(v1.x); //2 Console.WriteLine(r1.x); //5 注意这里变成了r2修改后的值 Console.WriteLine(v2.x); //5 Console.WriteLine(r2.x); //5 Console.ReadKey(); }首先我们定义一个一值类型与一个引用类型,内部都只有一个字段。用new操作符分配内存时,值类型v1的内存分配在了线程栈上,引用类型r1的内存分配在了托管堆上,在程序运行到第一次WriteLine输出时,看到的结果是一致的。但接下来声明两个新的对象并执行赋值时,这里的发生的事明显不同:虽然赋值操作都是拷贝线程栈上变量的内容,但由于值类型变量v1的栈内容就是ValType类型实例本身,而引用类型r1的栈内容是RefType对象实例在堆上的地址。所以赋值后的结果就是,v1和v2各保存了一份ValType类型实例,而r1和r2保存了同一块堆内存的地址。所以改变r2对象导致了r1对象的随同改变。下面是内存示意图:
![C 引用类型、值类型 - Complaint Free Wolrd - Complaint Free Wolrd](http://images.cnitblog.com/blog/279191/201306/22153249-e7a23a24569247e0bdaf42bcb94f27a1.png)
![C 引用类型、值类型 - Complaint Free Wolrd - Complaint Free Wolrd](http://images.cnitblog.com/blog/279191/201306/22153314-361375ed836d48f0962193436820a02d.png)
在托管堆中分配好内存,分配的内存量=值类型的各个字段所需的内存量+所有堆上对象都有的两个额外成员(类型对象指针和同步块索引)所需的内存量。 值类型的字段复制到新分配的内存。 返回对象的地址。 拆箱仅是获取一个指针的过程,该指针指向包含在一个对象中的原始值类型(数据字段)。虽然拆箱比装箱代价低,但实际在拆箱之后往往紧接着就是赋值操作(内存复制)。显然装箱和拆箱/复制会对应用程序的速度与内存消耗上产生不利影响,所以应该了解到这一点,并尽量避免装箱和拆箱操作。那么什么时候会发生装箱和拆箱,最直观的方法就是看生成的IL代码(IL对应指令是分别是box与unbox),比如下面的例子:
![C 引用类型、值类型 - Complaint Free Wolrd - Complaint Free Wolrd](http://images.cnitblog.com/blog/279191/201306/22153846-1cfa12e1d3d4424ab149acbf4103f566.png)
定义一个方法如果可接收引用类型或值类型时,尽量不要将参数定义为object,可以考虑通过重载定义多个版本或定义泛型方法。 原文地址
再聊一次值类型和引用类型 这是一个托管的值类型,表示array的一个片断, 在用做websocket接收数据的载体时我发现每次值传递后, 这个ArraySegment byte 貌似发生了变化。这勾起了我的探究欲。
第183天:引用类型和值类型 一、内存分配 1、数值型内存分配 2 var num1; //这个时候不进行内存分配 3 var num3=9;//分配内存 4 var num4=num3;//会不会分配 6 // 这里到底分配还是不分配内存?? 7 // 答案 --分配内存 彼此拥有独立的内存空间,互不影响 8 console.
c#值类型与引用类型区别 值类型对象的两种表示方式:未装箱和已装箱,引用类型总是处于已装箱 值类型从System.ValueType派生。该类型提供了与System.Object相同方法,但System.ValueType重写了Equals方法, 能在两个对象的自断值完全匹配的前提下返回true。
相关文章
- C#.NET 无法直接启动带有类库输出类型的项目怎么办
- c# 优化代码的一些规则——优先隐式类型[一]
- [c#基础]值类型和引用类型的Equals,==的区别
- c# 优化代码的一些规则——优先隐式类型[一]
- C# 对象比较(值类型、引用类型)
- 类型参数的约束(C# 编程指南)
- C# 格式化string类型的金额
- Oracle的Numer类型与C,C#数据类型对应关系
- C# 将一个对象转换为指定类型
- C#中如何比较2个string类型的日期大小?
- C# 引用类型、值类型
- C# 枚举类型
- C# 常用类库(字符串处理,汉字首字母拼音,注入攻击,缓存操作,Cookies操作,AES加密等)
- C#调用摄像头(AForge)实现扫描条码解析(Zxing)功能
- c# 将字符串转换为指定类型的值
- C# 字符串处理
- C# winform坐标系类型详解
- (76)C#里怎么样选择各种通用类型容器
- (52)C#里使用事件的例子
- C#/.NET CTS和CLS:公共类型系统和公共语言规范