栈和托管堆/值类型和引用类型/强制类型转换/装箱和拆箱[C#]
原文地址:http://www.cnblogs.com/xy8.cn/articles/1227228.html
一、栈和托管堆
通用类型系统(CTS)区分两种基本类型:值类型和引用类型。它们之间的根本区别在于它们在内存中的存储方式。.NET使用两种不同的物理内存块来存储数据—栈和托管堆。如下图所示:

值类型总是在内存中占用一个预定义的字节数(例如,int类型占4个字节,而string类型占用的字节数会根据字符串的长度不同而不同),当声明一个值类型变量时,会在栈中分配适当大小的内存(除了引用类型的值类型成员外,如类的int字段),内存中的这个空间用来存储变量所含的值。.NET维护一个栈指针,它包含栈中下一个可用内存空间的地址。当一个变量离开作用域时,栈指针向下移动被释放变量所占用的字节数,所以它仍指向下一个可用地址。
引用变量也利用栈,但这时栈包含的只是对另一个内存位置的引用,而不是实际值。这个位置是托管堆中的一个地址。和栈一样,它也维护一个指针,包含堆中下一个可用内存空间的地址。但是,堆不是先入后出的,因为对对象的引用可在我们的程序中传递(例如,作为参数传递给方法调用),堆中的对象不会在程序的一个预定点离开作用域。为了在不使用在堆中分配的内存时将它释放,.NET定期执行垃圾收集。垃圾收集器递归地检查应用程序中所有的对象引用。引用不再有效的对象使用的内存无法从程序中访问,该内存就可以回收。
二、类型层次结构
CTS定义了一种类型层次结构,该结构不仅描述了不同的预定义类型,还指出用户定义类型在层次结构中的位置。

三、引用类型
引用类型包含一个指针,指向堆中存储对象本身的位置。因为引用类型只包含引用,不包含实际的值,对方法体内参数所做的任何修改都将影响传递给方法调用的引用类型的变量。
下图显示了声明一个字符串变量并把它作为参数传递给一个方法时所发生的事情。
当声明字符串变量s1时,一个值被压入栈中,它指向栈中的一个位置。在上图中,引用存放在地址1243044中,而实际的字符串存放在堆的地址12262032中。当该字符串传递给一个方法时,在栈上对应输入参数声明了一个新的变量(这次是在地址1243032上),保存在引用变量,即堆中内存位置中的值被传递给这个新的变量。
委托是引用方法的一种引用类型,类似于C++中的函数指针(两者的主要区别在于委托包括调用其方法的对象)。
四、预定义的引用类型
有两种引用类型在C#中受到了特别的重视,它们的C#别名和预定义值类型的C#别名很相像。第一种是Object类(C#别名是object, o小写)。这是所有值类型和引用类型的最终基类。因为所有的类型派生自Object,所以可以把任何类型转换为Object类型,甚至值类型也可以转换。这个把值类型转换为Object的过程称为装箱。所有的值类型都派生自引用类型,在这件看似矛盾的事情背后,装箱的作用不可或缺。
第二种是String类。字符串代表一个固定不变的Unicode字符序列。这种不变性意味着,一旦在堆中分配了一个字符串,它的值将永远不会改变。如果值改变了,.NET就创建一个全新的String对象,并把它赋值给该变量。这意味着,字符串在很多方面都像值类型,而不像引用类型。如果把一个字符串传递给方法,然后在方法体内改变参数的值,这不会影响最初的字符串(当然,除非参数是按引用传递的)。C#提供了别名string(s小写)来代表System.String类。如果在代码中使用String,必须在代码一开始添加using System; 这一行。使用内建的别名string则不需要添加using System;
五、强制类型转换
long x=12345;
int k=(int) x; //发生收缩型强制类型转换
从较小数据类型到较大数据类型的转换称为扩展转换,否则称为收缩转换。编译器能进行隐式的扩展转换,对于收缩转换必须进行显式的强制性转换。因为收缩转换会导致丢失数据,在转换前我们要检查实际值是否超出目标类型的范围。另一个办法是使用checked运算符,如果转换时丢失数据将抛出一个错误。
强制类型转换即可针对值类型,又可针对引用类型。
六、装箱和拆箱(boxing/unboxing)
值类型和引用类型都是从Object类派生的。这意味着任何一个以对象为参数的方法,都可以给它传递一个值类型。相似地,值类型可以调用一个Object类方法:
int j=4;
string str=j.ToString();
这里是另一个强制类型转换的例子。您可能还记得,一个值类型变量包含存储在栈中的数据。您也许不明白值类型的变量如何调用一个引用类型的方法。答案是在一个称为装箱(boxing)的过程中,值类型变量被隐式转换为引用类型。从概念上来讲,装箱的过程就是对应值类型创建一个临时的引用类型的“箱子”。下面是IL代码:
IL_000: ldc.i4.4 //Load the int 4 onto the stack
IL_001: stloc.0 //Pop the value off the stack and into V_0
IL_002: ldloca.s V_0 //Push the address of variable V_0 onto the stack
//Call Int32::ToString()
IL_004: call instance string[mscorlib]System.Int32::ToString()
关键的语句是ldloca.s V_0,它加载指向V_0变量的一个托管指针。ToString()方法是在这个托管指针上调用,而不是在值本身调用。
还可以以下面正常的转换语法显式地将一个值装箱:
int j=4;
object ojb=(object) j;
使用相同的类型转换语法可以把装箱的变量转换回值类型:
int k=(int)obj;
对拆箱操作有一些限制。只能将显式装箱的变量进行拆箱。正常的强制转换中的限制在这里也适用。例如,如果把一个long型值装箱为一个对象,我们不能把该对象拆箱为一个int型值,虽然在拆箱后可以显式地把long转换为int:
long x=1000;
object obj=(object) x;
int i=(int)((long)obj);
装箱与拆箱示意图:

栈和托管堆/值类型和引用类型/强制类型转换/装箱和拆箱[C#]的更多相关文章
- 定义类+类实例化+属性+构造函数+匿名类型var+堆与栈+GC回收机制+值类型与引用类型
为了让编程更加清晰,把程序中的功能进行模块化划分,每个模块提供特定的功能,而且每个模块都是孤立的,这种模块化编程提供了非常大的多样性,大大增加了重用代码的机会. 面向对象编程也叫做OOP编程 简单来说 ...
- 6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱
引言 本篇文章主要介绍.NET中6个重要的概念:栈,堆,值类型,引用类型,装箱,拆箱.文章开始介绍当你声明一个变量时,编译器内部发生了什么,然后介绍两个重要的概念:栈和堆:最后介绍值类型和引用类型,并 ...
- [No0000136]6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱
引言 本篇文章主要介绍.NET中6个重要的概念:栈,堆,值类型,引用类型,装箱,拆箱.文章开始介绍当你声明一个变量时,编译器内部发生了什么,然后介绍两个重要的概念:栈和堆:最后介绍值类型和引用类型,并 ...
- .NET六大剑客:栈、堆、值类型、引用类型、装箱和拆箱
.NET六大剑客:栈.堆.值类型.引用类型.装箱和拆箱 一.“堆”,“栈”专区 这两个字我相信大家太熟悉了,甚至于米饭是什么?不知道...“堆”,“栈”是什么?哦,这个知道... 之前我也写过一篇堆栈 ...
- C# 托管与非托管类型 堆和栈 值类型与引用类型 装箱与拆箱
一.托管类型与非托管类型 1.托管类型 托管类型包括 引用类型 以及 包含有引用类型或托管类型成员的结构. 引用类型 含引用类型或托管类型成员(字段.自动实现 get 访问器的属性)的结构 // 托管 ...
- C# 值类型和引用类型
一.基本概念 C#只有两种数据类型:值类型和引用类型 值类型在线程栈分配空间,引用类型在托管堆分配空间 值类型转为引用类型称成为装箱,引用类型转为值类型称为拆箱 以下是值类型和引用类型对照表 从上图可 ...
- c#1所搭建的核心基础之值类型和引用类型
这个主题很重要,在.NET中做的一切其实都是在和一个值类型或者引用类型打交道. 现实世界中的值和引用 假定你在读一份非常棒的东西,希望一个朋友也去读他.于是你到复印室里复印了一份.这个时候他获得了属于 ...
- 浅谈.NET中的类型和装箱、拆箱原理
谈到装箱拆箱,大概的意思就是值类型和引用类型的相互转换呗---值类型到引用类型叫装箱,反之则叫拆箱.这当然没有问题,可是你只知道这么多,那么建议你花点时间看看楼主这篇文章 1. .NET中的类型 为了 ...
- 图解C#的值类型,引用类型,栈,堆,ref,out
C# 的类型系统可分为两种类型,一是值类型,一是引用类型,这个每个C#程序员都了解.还有托管堆,栈,ref,out等等概念也是每个C#程序员都会接触到的概念,也是C#程序员面试经常考到的知识,随便搜搜 ...
随机推荐
- 解决Xcode8 输出一对字符串问题
在Product->Scheme->Edit Scheme->Run->Environment Variables下添加键:OS_ACTIVITY_MODE, 值:Disabl ...
- css的存在形式以及优先级
css的存在形式以及优先级 css不仅仅可以在每个head标签中定义,而且也可以写在一个文件中,每个页面即可进行引用,这样可以做到重复利用. css文件的写法如下: common.css .c1{ h ...
- lesson5:利用jmeter来压测消息队列(activemq)
本文讲述了利用jmeter来压测消息队列,其中消息队列采用apache的activemq,jmeter本身是支持符合jms标准消息队列的压测,由于jmeter的官方sampler配置比较复杂,本文直接 ...
- NuGet学习笔记(2)——使用图形化界面打包自己的类库
上文NuGet学习笔记(1) 初识NuGet及快速安装使用说到NuGet相对于我们最重要的功能是能够搭建自己的NuGet服务器,实现公司内部类库的轻松共享更新.在安装好NuGet扩展后,我们已经能够通 ...
- 【iOS开发之OC和JS互调】
1.OC中调用JS代码 公司的移动端需要加载一个现有的网页,并且要在原网页要做一些小的调整,如将网页的标题改一下加载到手机的app上,此时就可以在app的oc代码中加入JS代码来实现.如下例子,我要加 ...
- Java 坦克小游戏心得
原本是闲得慌无聊才去尝试做这个项目的,因为小时候玩小霸王的游戏机,那个时候经常玩这个游戏吧,特别是喜欢那种自定义地图的模式,觉得自由度非常不错.总之关于这个游戏,想说的一大堆.鉴于能有个空闲的时间,打 ...
- Android -------- 序列化器生成xml文件
- 高性能ORM框架XLinq功能详细介绍
之前简单介绍了XLinq的一些功能,有很多功能都没有提到,现在给XLinq加了一些功能,这次把所有功能都介绍一遍. 设计目标 易用性 在使用一个框架的时候 应该没几个人会喜欢写一大堆的配置文件吧 也应 ...
- [.NET]Repeater控件使用技巧
1.控制Repeater表格中的按钮显隐 1.1 定义方法 public void Repeater1_ItemDataBinding(object sender, RepeaterItemEvent ...
- copy-on-write(写时拷贝技术)
今天看<Unix环境高级编程>的fork函数与vfork函数时,看见一个copy-on-write的名词,貌似以前也经常听见别人说过这个,但也一直不明白这究竟是什么东西.所以就好好在网上了 ...