<NET CLR via c# 第4版>笔记 第5章 基元类型、引用类型和值类型
5.1 编程语言的基元类型
- c#不管在什么操作系统上运行,int始终映射到System.Int32; long始终映射到System.Int64
- 可以通过checked/unchecked操作符/语句打开或关闭溢出检查,如:
byte b = 100;
b = checked((byte)(b + 200));
uint invalid = unchecked((uint)(-1));
checked {
b += 200;
}
- 在checked操作符或语句中调用方法,不会对该方法造成任何影响,如:
checked
{
//假定SomeMethod试图把400加载到一个Byte中
SomeMethod(400);
//SomeMethod可能会、也可能不会抛出OverflowException异常
//如果SomeMethod使用checked指令编译,就可能会抛出异常
//但这和当前的checked语句无关
}
尽量使用有符号数值类型(比如Int32和Int64)而不是无符号数值类型(比如UInt32和UInt64),这允许编译器检测更多的上溢/下溢错误.较少的强制类型转换也可以使代码更整洁,更易维护.
System.Decimal在CLR中不被认为是基元类型.处理速度慢于CLR基元类型.常用于不容许舍入误差的金融计算.checked和unchecked操作符,语句以及编译器开关对System.Decimal不起作用.如果Decimal值执行的运算是不安全的,肯定会抛出OverflowException异常.
5.2 引用类型和值类型
- 值类型分配在线程栈上,引用类型从托管堆分配.
- 所有值类型都隐式密封以防止将值类型用作其它引用类型或值类型的基类型.
- 将值类型变量赋给另一个值类型变量,会执行逐字段的复制.将引用类型的变量赋给另一个引用类型的变量只复制内存地址.
- 基于上一条,两个或多个引用类型变量能引用堆中同一个对象,对一个变量执行的操作可能影响到另一个变量引用的对象.相反,对值类型变量的操作不可能影响另一个值类型变量.
- 自定义struct类型时需要注意:
- 具有基元类型的行为--简单,成员不可变(建议全部字段标记为readonly);
- 不从其它类型继承,也不派生出其它任何类型;但可实现一个或多个接口;
- 类型的实例较小(16字节或更小),或不作为方法实参传递,也不从方法返回;
- 自定义值类型应该重写Equals和GetHashCode方法(默认实现有性能问题);
- 不能有新的虚方法,所有方法都不能是抽象的,所有方法都隐式密封(不可重写);
- 如不需要与非托管代码互操作,可为struct应用StructLayoutAttribute特性,并向构造器传递LayoutKind.Auto.
5.3 值类型的装和拆箱
- 装箱过程:
- 在托管堆中分配内存.分配的内存量是值类型各字段所需的内存量,还要加上托管堆所有对象都有的两个额外成员(类型对象指针和同步块索引)所需的内存量.
- 值类型的字段复制到新分配的堆内存.
- 返回对象地址.现在该地址是对象引用;值类型成了引用类型.
拆箱的代价比装箱低得多
拆箱时,只能转型为最初未装箱的值类型,否则会抛出InvalidCastException异常.
未装箱值类型没有同步块索引,不能使用System.Threading.Monitor类型的方法(或者C#lock语句)让多个线程同步对实例的访问.
派生值类型中,重写的虚方法如果调用基类的实现,会装箱,以便能够通过this指针将对一个堆对象的引用传给基方法.
调用非虚的,继承的方法时(比如GetType或MemberwiseClone),无如何都要对值类型进行装箱.因为这些方法由System.Object定义,要求this实参是指向堆对象的指针.
将值类型的未装箱实例转型为类型的某个接口时要对实例进行装箱.因为接口变量必须包含对堆对象的引用.
检查同一性(看两个引用是否指向同一个对象)务必调用ReferenceEquals,不应使用==操作符.
重写Equals需符合4个特征:
- Equals必须自反;x.Equals(x)肯定返回true
- Equals必须对称;x.Equals(y)和y.Equals(x)返回相同的值
- Equals必须可传递;x.Equals(y)返回true,y.Equals(z)返回true,则x.Equals(z)肯定返回true.
- Equals必须一致.比较的两个值不变,Equals返回值也不能变.
- 重写Equals可能还需要:
- 让类型实现
System.IEquatable<T>接口的Equals方法- 重载==和!=操作符方法
Q:以下代码的输出结果是?有几次装箱操作?
static void Main()
{
int v = 5;
object o = v;
v = 123;
Console.WriteLine(v + "," + (int)o);
}
A:显示"123,5".有3次装箱操作.上面代码合理的写法是:Console.WriteLine(v.Tostring()+","+o) .这样只装箱1次.
5.4 对象哈希码
计算类型实例的哈希码,需遵守以下规则:
- 提供良好的随机分布,使哈希表获得最佳性能;
- 可在算法中调用基类的GetHashCode方法,并包含返回值.但不要调用Object或ValueType的GetHashCode方法,因为两者实现性能不好.
- 至少使用一个实例字段.
- 算法使用的字段应该不可变(使用readonly标记,并在对象构造时初始化)
- 算法执行速度尽量快
- 包含相同值的不同对象应该返回相同哈希码
- 千万不要对哈希码进行执久化,因为不同的.net版本,算法可能不一样,得到的哈希码也可能不一样
5.5 dynamic基元类型
- 编译器不允许写代码将表达式从Object隐式转型为其它类型;但允许使用隐式转型语法将表达式从dynamic转型为其它类型:
object o1=123;
int n1=o1; //错误 dynamic d1=123;
int n3=d1; //正确
- var只是简化语法,只能在方法内部声明局部变量;dynamic表达式其实是和System.Object一样的类型.
- 不能将lambda表达式或匿名方法作为实参传给dynamic方法调用,因为编译器推断不了要使用的类型.
- 使用dynamic会带来额外的开销,如果程序中只是一,两个地方需要动态行为,不如使用传统方法,即调用反射方法(如果是托管对象),或者进行手动类型转换(如果是COM对象)
<NET CLR via c# 第4版>笔记 第5章 基元类型、引用类型和值类型的更多相关文章
- <NET CLR via c# 第4版>笔记 第13章 接口
13.1 类和接口继承 13.2 定义接口 C#用 interface 关键字定义接口.接口中可定义方法,事件,无参属性和有参属性(C#的索引器),但不能定义任何构造器方法,也不能定义任何实例字段. ...
- <NET CLR via c# 第4版>笔记 第12章 泛型
泛型优势: 源代码保护 使用泛型算法的开发人员不需要访问算法的源代码.(使用c++模板的泛型技术,算法的源代码必须提供给使用算法的用户) 类型安全 向List<DateTime>实例添加一 ...
- <NET CLR via c# 第4版>笔记 第8章 方法
8.1 实例构造器和类(引用类型) 构造引用类型的对象时,在调用类型的实例构造器之前,为对象分配的内存总是先被归零 .没有被构造器显式重写的所有字段都保证获得 0 或 null 值. 构造器不能被继承 ...
- <NET CLR via c# 第4版>笔记 第19章 可空值类型
System.Nullable<T> 是结构. 19.1 C# 对可空值类型的支持 C# 允许用问号表示法来声明可空值类型,如: Int32? x = 5; Int32? y = null ...
- <NET CLR via c# 第4版>笔记 第17章 委托
17.1 初识委托 .net 通过委托来提供回调函数机制. 委托确保回调方法是类型安全的. 委托允许顺序调用多个方法. 17.2 用委托回调静态方法 将方法绑定到委托时,C# 和 CLR 都允许引用类 ...
- <NET CLR via c# 第4版>笔记 第16章 数组
//创建一个一维数组 int[] myIntegers; //声明一个数组引用 myIntegers = new int[100]; //创建含有100个int的数组 //创建一个二维数组 doubl ...
- <NET CLR via c# 第4版>笔记 第7章 常量和字段
7.1 常量 常量 是值从不变化的符号.定义常量符号时,它的值必须能够在编译时确定. 只能定义编译器识别的基元类型的常量,如果是非基元类型,需把值设为null. 常量的值直接嵌入代码,所以不能获取常量 ...
- <NET CLR via c# 第4版>笔记 第9章 参数
9.1 可选参数和命名参数 class Program { private static int s_n = 0; private static void M(int x = 9, string s ...
- C#学习笔记10:Try-catch的用法和引用类型、值类型整理
Try-Catch: 将可能发生异常的代码放到try中,在catch中进行捕获. 如果try中有一行代码发生了异常,那么这行代码后面的代码不会再被执行了. Try写完了以后,紧接着就要写Catch ...
随机推荐
- python初识(三)
目录: 字符编码 文件操作 循环结构拾遗 函数 整体介绍 函数的参数 函数的返回值 函数的调用 自定义函数 一.字符编码 1.了解字符编码的知识储备 a. 文件编辑存取文件的原理(nodepad++, ...
- Openstack(五)Memcache- repcached集群
5.1.1下载Memcache- repcached并解压 # wget http://downloads.sourceforge.net/repcached/memcached-1.2.8-repc ...
- 2.6 The Object Model -- Bindings
一个binding在两个属性之间创建一个链接,当一个改变时,另外一个被自动更新为一个新的值. bindings可以在同一个对象中连接两个属性,或者用在两个不同的对象中. 不像大多数框架一样包含某种形式 ...
- #C++初学记录(并查集)
并查集 题目 今天是伊格那丢的生日.他邀请了很多朋友.现在该吃晚饭了.伊格那丢想知道他至少需要多少张桌子.你必须注意到并不是所有的朋友都认识对方,而且所有的朋友都不想和陌生人待在一起.这个问题的一个重 ...
- R语言统计词频 画词云
原始数据: 程序: #统计词频 library(wordcloud) # F:/master2017/ch4/weibo170.cut.txt text <- readLines("F ...
- la3523 白书例题 圆桌骑士 双联通分量+二分图
具体题解看大白书P316 #include <iostream> #include <algorithm> #include <vector> #include & ...
- HIVE: 自定义TextInputFormat (旧版MapReduceAPI ok, 新版MapReduceAPI实现有BUG?)
我们的输入文件 hello0, 内容如下: xiaowang 28 shanghai@_@zhangsan 38 beijing@_@someone 100 unknown 逻辑上有3条记录, 它们以 ...
- 一篇关于Redis的很不错的文章,转载保存下
绝大部分写业务的程序员,在实际开发中使用 Redis 的时候,只会 Set Value 和 Get Value 两个操作,对 Redis 整体缺乏一个认知.这里对 Redis 常见问题做一个总结,解决 ...
- 常用php操作redis命令整理(一)通用及字符串类型
Key相关操作 TYPE 类型检测,字符串返回string,列表返回 list,set表返回set/zset,hash表返回hash,key不存在返回0 <?php echo $redis-&g ...
- 20145221 《Java程序设计》第六周学习总结
20145221 <Java程序设计>第六周学习总结 教材学习内容总结 第十一章部分 - 输入与输出 文件的读写 网络上传数据的基础 同样要先掌握父类中方法,核心类如下: 以上则是老师提出 ...