前面介绍了基本的类型,接下来我们讲讲类型的转换

值类型的两种表现形式:未装箱和已装箱 ,而引用类型总是处于装箱形式

int count = 10;

object obj = count;

装箱:值类型转换为引用类型,C#编译器可以自动完成装箱操作

a.在托管堆中分配好内存。内存量 = 值类型字段的内存量 + 类型对象指针 + 同步索引块

b.将值类型的字段复制到新分配的堆地址中

c.返回对象的地址

int count1 = (int)obj;

拆箱:引用类型转换为值类型,需要显式完成

a.获取obj对象的引用

b.将值从堆中复制到基于栈的值类型实例 coun1中

c.如果obj的引用地址为null,则抛出 NullReferenceException 异常

d.如果obj引用指向的的对象不是int类型的已装箱的实例,抛出 InvalidCastException 异常

千言万语,我只想上代码!

Int32  a = 5 ; 

object o = a;

Int16  b = (Int16) o ;

上面拆箱能否成功?

答案是不能,因为Int32的范围比Int16大,转换的时候就抛异常了。

接下来这个例子很有意思,好多人估计都不知所以然。来,做好了,我们继续开车!

Int32 v = 5 ;

object o = v ;

v = 123;

Console.WriteLine( v + “and “ + (Int32) o);  

问题:上面例子发生了多少次装箱操作 ?

有的人看到代码就一拍脑袋说:1次,2次......,好吧,这么说我不怪你,因为即使工作几年的老司机也不一定能一眼看出来是几次装箱。

但是当你觉得不太确定的时候就要去找出答案,拨开迷雾才能看到真相。我们来个简单粗暴的方法,看IL代码:

清楚了吧,明白了吧,简直是一目了然啊,三个box,那就是三次装箱了,中间还发生过一次拆箱,那就是Int32转的那一次

那么为什么是三次呢?

第一次很明显,第二次和第三次是发生在 Console.WriteLine 里面的,我们看到箭头标注的地方,为什么会调用了 string.Concat方法呢,首先我们知道这个函数是用来拼接字符串的,那就稍微有点明白了,我们可以看到现在给WriteLine 方法传的是3个参数,那实际上WriteLine 有没有三个参数的重载呢?答案是有,但是很遗憾,并不是符合我们给的三个参数类型的。那怎么办呢,我们知道编译器是非常聪明的,它非常确信的知道我们传入的三个参数中第二个是个字符串,它会默认调用WriteLine 的string重载方法,那这样的话就要求传入的是一个完整的string对象,而我们是三个,那就需要把三个参数合成一个,于是乎编译器很聪明的自动调用了string.Concat 方法,接收三个参数,而Concat方法接收的三个参数都是object的,所以一切都明白了,第一个参数装箱一次,第三个参数又装箱一次,所以总共就是三次装箱。

上面的代码怎么能减少装箱次数?最少用几次装箱?各位看官自己想想吧,这个已经很简单了

分析完上边的例子,按照惯例我们接着上代码:

Int32 v = 5 ;

object o = v ;

v = 123;

Console.WriteLine( v );

v = (Int32) o;

Console.WriteLine( v );  

同样的问题:上述代码输出什么结果?发生了多少次装箱 ?

答案会是一样吗?自己思考一下吧,如果不确定可以在评论里说,我会给出分析。

类型转换

对象类型转换:

a.将对象转换为它对应的任何基类型,反之则不行

b.使用as操作符来转型,强制类型转换

基元类型转换:

隐式转换:编译器确定转换“安全”的时候,才允许隐式转换。对于数值类型,不安全意味着转换可能会丢失精度或者数量级

int32 a = 5 ;

int64 b  = a ;

显式转换:显示指定需要转换的类型

Byte c = (Byte) a ;

对基元类型执行的许多算术运算符都可能造成溢出,比如下面代码:

Byte b = 100 ;

b = (Byte) (b + 200)

因为Byte的默认长度是255,而相加之后的结果已经超出了最大长度了,但是运行并没有报错,这是为什么呢?答案是编译器在作怪

大多数的溢出都是悄悄发生的,编译器并不会报错,但是大多数情况下都会导致程序行为异常

C# 编译器允许开发人员决定如何处理溢出,编译器溢出检查默认是关闭的,我们可以手动打开检查溢出的开关

为了处理溢出,我们讲讲下面这两个操作符

checked 和 unchecked 操作符

Byte b = 100 ;
b = checked((Byte) (b + 200)); // 抛出OverflowException 异常 checked 语句 checked{ //开始一个checked块
Byte b = 100 ;
b =(Byte) (b + 200) ; // 溢出检查
} // 结束一个checked块

 

相信大家已经看得很清楚了,加了checked之后就会抛出异常,而恰巧编译器又是默认的unchecked。

那么checked和unchecked 本质上的区别是什么呢? 据说按惯例我又要上代码了,来,我们继续

本质区别就是生成的IL 指令不一样 ,指令决定了是否检查溢出

checked:add.ovf           unchecked:add

最后我们再来说说创建一个对象的过程究竟发生了什么事:

创建类型的对象

Person person = new Person();

new 做了什么事情?

1.计算类型及所有基类型中定义的所有实例字段需要的字节数 (堆上的每个对象都需要一些额外的成员信息:类型对象指针+同步索引块,这些成员用于CLR管理对象,会计入对象的大小)

2.从托管堆中分配指定类型要求的字节数,从而分配对象的内存,分配的所有字节都为0

3.初始化对象的类型对象指针 和 同步索引块

4. 调用类型的实例构造器,同时初始化类型的实例字段,最终调用的都是基类的构造器

5.返回指向新建对象的引用地址

垃圾回收器检查托管堆中是否有不再使用的任何对象就回收

最后留个问题,欢迎一起讨论:new 是创建对象,分配内存,如果创建完之后发现多余了,是否可以delete掉对象 ?

其实还想说一句:一直以来.NET程序员备受鄙视,因为微软麻麻对我们太好了,基本不需要我们做什么,编译器和CLR已经替我们做了太多的事情了,导致我们就只会用,只知道怎么用而不是到为什么,这对我们的长期发展来说是非常不好的,所以希望大家有时间多看看底层的东西,多看看IL代码,搞清楚编译器在中间干了什么事情,这是很重要的。

再给大家推荐一本书:《C# Via CLR》讲的非常好的一本书,很底层

C# 类型基础(下)的更多相关文章

  1. 《C#从现象到本质》读书笔记(三)第3章C#类型基础(下)

    <C#从现象到本质>读书笔记第3章C#类型基础(下) 常量以关键字const修饰.C#支持静态字段(类型字段)和实例字段. 无参属性的get方法不支持参数,而有参属性的get方法支持传入一 ...

  2. swift:类型转换(is用作判断检测、as用作类型向下转换)

    类型转换是一种检查类实例的方式,并且哦或者也是让实例作为它的父类或者子类的一种方式.   类型转换在Swift中使用is 和 as操作符实现.这两个操作符提供了一种简单达意的方式去检查值的类型或者转换 ...

  3. C#学习笔记——面向对象、面向组件以及类型基础

    C#学习笔记——面向对象.面向组件以及类型基础 目录 一 面向对象与面向组件 二 基元类型与 new 操作 三 值类型与引用类型 四 类型转换 五 相等性与同一性 六 对象哈希码 一 面向对象与面向组 ...

  4. 【Xamarin开发 Android 系列 7】 Android 结构基础(下)

    原文:[Xamarin开发 Android 系列 7] Android 结构基础(下) *******前期我们不打算进行太深入的东西,省的吓跑刚进门的,感觉门槛高,so,我们一开始就是跑马灯一样,向前 ...

  5. [CLR via C#]4. 类型基础及类型、对象、栈和堆运行时的相互联系

    原文:[CLR via C#]4. 类型基础及类型.对象.栈和堆运行时的相互联系 CLR要求所有类型最终都要从System.Object派生.也就是所,下面的两个定义是完全相同的, //隐式派生自Sy ...

  6. [No0000B9]C# 类型基础 值类型和引用类型 及其 对象复制 浅度复制vs深度复制 深入研究2

    接上[No0000B5]C# 类型基础 值类型和引用类型 及其 对象判等 深入研究1 对象复制 有的时候,创建一个对象可能会非常耗时,比如对象需要从远程数据库中获取数据来填充,又或者创建对象需要读取硬 ...

  7. [No0000B5]C# 类型基础 值类型和引用类型 及其 对象判等 深入研究1

    引言 本文之初的目的是讲述设计模式中的 Prototype(原型)模式,但是如果想较清楚地弄明白这个模式,需要了解对象克隆(Object Clone),Clone其实也就是对象复制.复制又分为了浅度复 ...

  8. CLR-基元类型以及溢出检查 (CLR-Via-C#) 类型基础

    CLR-基元类型以及溢出检查   =========(CLR via C#阅读笔记)======== 基元类型(primitive type): 基元类型也不做过多的解释,举个例子即可清晰的辨别 在j ...

  9. C++ Primer 学习笔记_32_STL实践与分析(6) --再谈string类型(下)

    STL实践与分析 --再谈string类型(下) 四.string类型的查找操作 string类型提供了6种查找函数,每种函数以不同形式的find命名.这些操作所有返回string::size_typ ...

  10. robotframework文本类型的下拉框

    对于下拉框定位和输入,这里主要遇到有两种类型的下拉选择. 其中一个类型是select-options格式,如图 这种方式的定位可以使用select from list by value或select ...

随机推荐

  1. 使用 GStreamer appsrc 等插件实现视频音频混流,录制和推流

    目前在做的在线直播教室,需要将老师分享的屏幕和老师的声音.学生的声音录制为一个视频文件,以便学生上课后还可以再看回放. 直播服务我们采用的是腾讯的视频服务,有现成的 SDK 可以用.但 SDK 自带的 ...

  2. BZOJ:4827: [Hnoi2017]礼物

    [问题描述] 我的室友最近喜欢上了一个可爱的小女生.马上就要到她的生日了,他决定买一对情侣手 环,一个留给自己,一个送给她.每个手环上各有 n 个装饰物,并且每个装饰物都有一定的亮度. 但是在她生日的 ...

  3. HDU5131-Song Jiang's rank list HDU5135-Little Zu Chongzhi's Triangles(大佬写的)

    Song Jiang's rank list Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 512000/512000 K (Java ...

  4. hdu_1026(最短路)

    题意:最快出去的路径 题解:一看就是一个很简单的最短路,用优先队列写个spfa就过了,但是没想到水了个(n*n)^2的算法也过了...愿意很简单,因为是个稀疏图 敲完以后一百度发现就是个简单的bfs+ ...

  5. poj_2195Going Home(最小费用最大流)

    poj_2195Going Home(最小费用最大流) 标签: 最小费用最大流 题目链接 题意: 有n*m的矩阵,H表示这个点是一个房子,m表示这个点是一个人,现在每一个人需要走入一个房间,已经知道的 ...

  6. c++(线性堆栈)

    前面我们讲到了队列,今天我们接着讨论另外一种数据结构:堆栈.堆栈几乎是程序设计的命脉,没有堆栈就没有函数调用,当然也就没有软件设计.那么堆栈有什么特殊的属性呢?其实,堆栈的属性主要表现在下面两个方面: ...

  7. MySQL基础----动态SQL语句

    尊重原创:http://blog.csdn.net/abc19900828/article/details/39501643   动态sql语句基本语法 1 :普通SQL语句可以用Exec执行 eg: ...

  8. HDU 1232 畅通工程(模板——并查集)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1232 Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出 ...

  9. 语义化版本控制规范(SemVer)

    摘自: http://semver.org/lang/zh-CN/ 简介 在软件管理的领域里存在着被称作"依赖地狱"的死亡之谷,系统规模越大,加入的套件越多,你就越有可能在未来的某 ...

  10. 快速搭建appium自动测试环境

    首先申明本文是基本于Python与Android来快速搭建Appium自动化测试环境: 主要分为以下几个步骤: 前提条件: 1)安装与配置python环境,打开 Python官网,找到"Do ...