跟小静读CLR via C#(02)-基元类型、引用类型、值类型
跟小静读CLR via C#(02)-基元类型、引用类型、值类型
一、 基元类型
编译器能够直接支持的数据类型叫做基元类型。例如int, string等。基元类型和.NET框架类库FCL存在着直接的映射关系。
string和String?
面试的时候曾经被问到过这个问题,C#中的基元类型string实际上对应了System.String(FCL)类型,所以两者使用的时候没有什么不同。
类型转换
编译器能够在基元类型之间进行显式或隐式转换。如果转换是安全的,也就是转换过程不会造成数据丢失,则可以直接采用隐式转换。如果是不安全的,则必须采用显式转换。
Int32 a=5;
Int64 b=a;
Int32 c=(Int32)b;
二、 引用类型和值类型
引用类型和值类型的区别:
引用类型 |
值类型 |
从托管堆中分配 |
从线程的堆栈中分配 |
对象考虑垃圾回收机制 |
不考虑垃圾回收机制 |
所有类都是引用 |
结构或枚举都是值类型 |
继承自System.ValueType |
|
只有装箱形式 |
有两种形式:装箱和未装箱 |
可以继承和派生 |
不能作为基类,不能有虚方法 |
引用类型变量初始化时默认为null |
初始化时默认为0值 |
复制时只拷贝内存地址 |
复制时“字段对字段”的拷贝 |
结构体直接继承自System.ValueType;而枚举直接继承自System.Enum, Enum类又直接继承自System.ValueType。
下面通过例子看一下他们的区别:
首先定义类和结构体:
class SomeRef { public Int32 x; }
struct SomeVal { public Int32 x; }
SomeRef r1 = new SomeRef(); // 分配到堆
SomeVal v1 = new SomeVal(); // 分配到栈
r1.x =5; // 所引用的堆空间内数据修改
v1.x =5; // 直接在栈上复赋值
Console.WriteLine(r1.x); // "5"
Console.WriteLine(v1.x); // "5"
SomeRef r2 = r1; //只把指针复制给了r2
SomeVal v2 = v1; // 在栈上分配空间,并且将变量内容进行复制
r1.x = 8; // r1指向(也是r2指向)的内容修改
v1.x = 9; // 只修改v1内容,v2内容不会受影响
Console.WriteLine(r1.x); // "8"
Console.WriteLine(r2.x); // "8"
Console.WriteLine(v1.x); // "9"
Console.WriteLine(v2.x); // "5"
看看下图的内存分配情况,就一目了然了。
三、 值类型的装箱与拆箱
1. 装箱过程?
装箱:将值类型转换为引用类型。当我们把值类型参数传递给需要引用类型参数的方法时,会自动进行装箱操作。过程如下:
- 从托管堆为要生成的引用类型分配大小。大小为:值类型实例本身的大小+额外空间(方法表指针和SyncBlockIndex)。
- 将值类型字段拷贝到刚刚分配的内存中。
- 返回托管堆中新分配内存的地址。也就是指向对象的引用。
2. 拆箱过程?
拆箱:获取指向对象中包含的值类型部分的指针。一般拆箱之后会进行字段拷贝操作,两个操作加起来才是真正与装箱互反的操作。过程如下:
- 如果引用为Null,则抛出NullReferenceException异常。
- 如果引用对象不是一个期望值类型的已装箱对象,会抛出InvalidCastException异常。
- 返回一个指向包含在已装箱对象中值类型部分的指针。
3. 实例
- 拆箱的转型结果必须是它原来未装箱时的类型。
public static void Main() {
Int32 x = 5;
Object o = x; // 装箱
Int16 y = (Int16) o; // 拆箱,抛出InvalidCastException异常
}
修正:Int16 z=(Int16)(Int32)o;//拆箱成功
- 这段代码进行了几次装箱?
public static void Main() {
Int32 v = 5; // 创建值变量
Object o = v; // 装箱
v = 123; // Changes the unboxed value to 123
Console.WriteLine(v + ", " + (Int32) o); // Displays "123, 5" ,装箱两次
}
上面的代码进行了3次装箱,最后一行中v被装箱为引用类型,o首先被拆箱然后再装箱为引用类型。
我们看一下它的IL代码:
字符串连接实际上调用的是Concat方法,该方法的参数要求是object,因此各参数都要转换成object类型。
优化:
Console.WriteLine(v.ToString()+”,”+o);//装箱0次
我们要尽量少的进行值类型的装箱拆箱操作,以提高程序性能。
跟小静读CLR via C#(02)-基元类型、引用类型、值类型相关推荐
- 跟小静读CLR via C#-开篇及目录
最近正在拜读Jeffrey大师的.NET巨作-<CLR via C#>.好书就像一杯茶,需要静下心来慢慢品.在写笔记的过程中,也加入了自己的思考:在和园友们的交流中,理解也不断的加深. 目 ...
- 跟小静读CLR via C#(05)- 访问限定、数据成员
跟小静读CLR via C#(05)- 访问限定.数据成员 今天跟大家分享一下关于访问限定和数据成员的知识.主要包括以下两点: Abstract, sealed, virtual, new, over ...
- 跟小静读CLR via C#(06)- 构造器
跟小静读CLR via C#(06)- 构造器 最近忙着看新还珠,好几天不学习了.玩物丧志啊,罪过罪过. 今天总结的是类构造器的知识,其实这方面的文章蛮多的,可还是觉得亲自写一下对自己的思考和认识会有 ...
- 跟小静读CLR via C#(18)——Enum
1. Enum定义 枚举类型是经常用的一种"名称/值"的形式,例如: public enum FeedbackStatus { New, ...
- 跟小静读CLR via C#(16)--泛型
泛型就像是一个模板,常常定义一些通用的算法,具体调用时再替换成实际的数据类型,提高了代码的可重用性. 一.初识泛型 1. 简单实例 以最常用的FCL中的泛型List<T >为例: stat ...
- 跟小静读CLR via C#(10)-参数
最近几天学了参数,其实对于参数本不陌生,从上学学C语言基础的时候就学过了.不过细心学下来,还是收获了一些. 一. 命名参数.可选参数 命名参数和可选参数是在Visual C#2010中引入的新特性. ...
- 跟小静学CLR via C#(12)-委托Delegate
本来按照进度应该学习事件了,可总觉得应该委托在前,事件在后,才好理解. 委托是一个类,它提供了回调函数机制,而且是类型安全的.使用委托可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数 ...
- 跟小静读《jQuery权威指南》——目录
前言 2014年开始了,年底给自己制订的学习计划,第一步先从学习<jQuery权威指南>开始. jQuery大家都很比较熟悉,但是我经常是边用的时候边对照着API,这次找本书通读一遍,记录 ...
- 【深入理解CLR 六】基元类型、引用类型和值类型
最近工作的事情比较忙,导致CLR很久没有更新了,恰巧周五听了涛涛的关于GC和内存管理的技术分享,想了下自己对CLR的学习得跟上,另外之前武哥推荐了一本书叫<码农翻身>,是一个IBM架构师写 ...
最新文章
- Bazel入门教程:编译C++项目
- Character流与Byte流的区别
- Nilearn中的Matplotlib颜色图
- [转载]交换机背板带宽计算方法
- jstl标签的用法 fn标签
- 【数字信号处理】傅里叶变换性质 ( 频域函数的共轭对称分解 | 序列的傅里叶变换 | 傅里叶变换的共轭对称 | 傅里叶变换的共轭反对称 )
- jodd-StringTemplateParser使用
- Java未被捕获的异常
- python刷题总结_【python刷题】差分数组
- 转:传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确 .
- Kerberos 基本命令 - 持续更新
- 【转载】IDEA中Git的更新/提交/还原方法
- 如何正确使用广告素材、优化Facebook广告
- Android,EditText,InuputType
- 查询各个商品分类中各有多少商品的SQL语句
- 《Mastering opencv....读书笔记》基于标记的虚拟现实
- Google拒绝因搜索不良信息被传唤
- 高并发架构系列:最全消息队列有哪些?详解消息队列的选型与应用
- 尾纤SC、ST、FC、LC区分
- 3.6 矩阵秩的其它重要关系