checked 和 unchecked 基元类型操作
对基元类型执行的许多算术运算都可能造成溢出:
Byte b = ;
b = (Byte) (b + ); // b 现在包含 44(或者十六进制值 2C)
重要提示:执行上述算术运算时,第一步要求所有操作数都扩大为 32 位值(或者 64 位值,如果任何操作数需要超过 32 位来表示的话)。所以, b 和 200(这两个值都不超过 32 位) 首先转换成 32 位值,然后加到一起。结果是一个 32 位值(十进制 300,或十六进制 12C)。该值在存回变量 b 之前,必须转型为一个 Byte。
C#不会隐式执行这个转型操作,这正是第二行代码需要强制转换为 Byte 的原因。
在大多数编程情形中,这种静悄悄发生的溢出是我们不希望的。如果没有检测到这种溢出,会导致应用程序行为失常。但在极少数编程情形中,比如计算一个哈希值或者校验和,这种溢出不仅是可以接受的,还是我们希望的。
不同语言以不同方式处理溢出。 C 和 C++不将溢出视为错误,并允许值回滚( wrap) 39;应用程序将“若无其事”地运行。相反, Microsoft Visual Basic 总是将溢出视为错误,并会在检测到溢出时抛出一个异常。
CLR 提供了一些特殊的 IL 指令,允许编译器选择它认为最恰当的行为。 CLR 有一个 add 指令,作用是将两个值加到一起,但不执行溢出检查。 CLR 还有一个 add.ovf 指令,作用也是将两个值加到一起,但会在发生溢出时抛出一个 System.OverflowException 异常。除了用于加法运算的这两个 IL 指令, CLR 还为减、乘和数据转换提供了类似的 IL 指令,分别是 sub/sub.ovf, mul/mul.ovf 和 conv/conv.ovf。
C#允许程序员自己决定如何处理溢出。溢出检查默认是关闭的。也就是说,编译器在生成 IL 代码时,会自动使用加、减、乘以及转换指令的不含溢出检查的版本。这样的结果是代码能够更快地运行——但是,开发人员必须保证不会发生溢出,或者他们的代码能预见到这些溢出。
让 C#编译器控制溢出的一个办法是使用 /checked+编译器开关。这个开关指示编译器在生成代码时,使用加、减、乘和转换指令的溢出检查版本。这样生成的代码在执行时会稍慢一些,因为 CLR 会检查这些运算,判断是否会发生溢出。如果发生溢出, CLR 会抛出一个 OverflowException 异常。
除了全局性地打开或关闭溢出检查,程序员还可在代码的特定区域控制溢出检查。 C#通过提供 checked和 unchecked 操作符来实现这种灵活性。 下面是一个使用了 unchecked 操作符的例子:
UInt32 invalid = unchecked((UInt32) (-)); // OK
下例则使用了 checked 操作符:
Byte b = ;
b = checked((Byte) (b + )); // 抛出 OverflowException 异常
在这个例子中, b 和 200 首先转换成 32 位值,然后加到一起,结果是 300。然后,因为显式转型的存在, 300 被转换成一个 Byte,这造成一个OverflowException 异常。如果 Byte 是在 checked 操作符的外部转
型的,则不会发生异常:
b = (Byte) checked(b + ); // b 包含 44;不会抛出 OverflowException 异常
除了 checked 和 unchecked 操作符, C#还支持 checked 和 unchecked 语句,它们造成一个块中的所有表达式都进行或不进行溢出检查:
checked { // 开始一个 checked 块
Byte b = ;
b = (Byte) (b + ); // 该表达式会进行溢出检查
} // 结束一个 checked
事实上,如果使用了一个 checked 语句块,就可以将+=操作符用于 Byte,从而稍微简化一下代码:
checked { // 开始一个 checked 块
Byte b = ;
b += ; // 该表达式会进行溢出检查
}
重要提示:由于 checked 操作符和 checked 语句唯一的作用就是决定生成哪一个版本的加、减、乘和数据转换 IL 指令,所以在一个 checked 操作符或者语句中调用一个方法,不会对该方法造成任何影响。
重要提示:
System.Decimal 类型是一个非常特殊的类型。虽然许多编程语言(包括 C#和 Visual Basic) 都将 Decimal视为一个基元类型,但 CLR 则不然。这意味着 CLR 没有相应的 IL 指令来决定如何处理一个 Decimal 值。在.NETFramework SDK 文档中查看 Decimal 类型可以看出,它提供了一系列 public static 方法,包括 Add, Subtract,Multiply, Divide 等。除此之外, Decimal 类型还为+, -, *, /等提供了操作符重载方法。
编译使用了 Decimal 值的程序时,编译器会生成代码来调用 Decimal 的成员,并通过这些成员来执行实际的运算。这意味着 Decimal 值的处理速度慢于 CLR 基元类型的值的处理速度。另外,由于没有相应的IL 指令来处理 Decimal 值,所以 checked 和 unchecked 操作符、 语句以及编译器开关都失去了效用。 如果对
Decimal 值执行的运算是不安全的,肯定会抛出一个 OverflowException 异常。
类似地, System.Numerics.BigInteger 类型也在内部使用一个 UInt32 数组来表示一个任意大的整数,它的值没有上限和下限。因此,对 BigInteger 执行的运算永远不会造成 OverflowException 异常。然而,如果值 太 大 , 而 且 没 有 足 够 多 的 内 存 来 改 变 数 组 的 大 小 , 对 BigInteger 的 运 算 可 能 抛 出 一 个OutOfMemoryException 异常。
checked 和 unchecked 基元类型操作的更多相关文章
- 《CLR via C#》读书笔记--基元类型、引用类型和值类型
编程语言的基元类型 编译器直接支持的数据类型称为基元类型.基元类型直接映射到Framework类库中存在的类型.例如:C#中的int直接映射到System.Int32类型.下表给出了C#基元类型与对应 ...
- 【C#进阶系列】05 基元类型、引用类型和值类型
基元类型和FCL类型 FCL类型就是指Int32这种类型,这是CLR支持的类型. 而基元类型就是指int这种类型,这是C#编译器支持的,实际上在编译后,还是会被转为Int32类型. 而且学过C的朋友 ...
- 重温CLR(四)基元类型、引用类型、值类型
编程语言的基元类型 编译器直接支持的数据类型称为基元类型(primitive type).基元类型直接映射到framework类型(fcl)中存在的类型. 下表列出fcl类型 从另一个角度,可以认为C ...
- 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_基元类型(三)
[checked 和 unchecked 基元类型操作] 1.第一种使用方式 UInt32 invalid = )); //OK,不会抛异常 Byte b = ; b = ));//抛出Overflo ...
- .net 基元类型,引用类型和值类型
基元类型(primitive type): 编译器直接支持的数据类型称为基元类型(primitive type). string 与 String: 由于C#中的string (一个关键字)直接映射到 ...
- System.Buffer 以字节数组(Byte[])操作基元类型数据
1. Buffer.ByteLength:计算基元类型数组累计有多少字节组成. 该方法结果等于"基元类型字节长度 * 数组长度" , , }; , , }; , , }; Cons ...
- 《CLR via C#》读书笔记(5)基元类型、引用类型和值类型
5.1 基元类型 编译器直接支持的数据类型称为基元类型(primitive type). 以下4行到吗生成完全相同的IL int a = 0; //最方便的语法 System.Int32 b = 0; ...
- 【CLR Via C#】第5章 基元类型、引用类型、值类型
第二遍看这本书,决定记录一下加深印象. 1,基元类型 什么事基元类型?基元类型是直接映射到FrameWork类库(FCL)中存在的类型,编译器直接支持的数据类型.比如int直接映射到System.In ...
- [CLR via C#]5.1 基元类型
原文:[CLR via C#]5.1 基元类型 某些数据类在开发中非常常用,以至于许多编译器允许代码已简化的语法来操作它们.例如可以使用以下语法来分配一个整数: System.Int32 a = ne ...
随机推荐
- VC驿站黑客编程(关机,重新启动,注销)
此程序在VS2013下编译通过,假设换到编译器,大家能够稍作改动使用 #include<Windows.h> #include<tchar.h> #include"r ...
- mybatis 使用注解实现多参数传递
若映射器中的方法只有一个参数,则在对应的SQL语句中,可以采用#{参数名}的方式来引用此参数,以前的例子多属于此类.但这种方法却不适用于需要传递多个参数的情况,今天就来介绍如何使用注解传递多个参数 一 ...
- python加密包
利用pycrypto包进行AES.DES.MD5等加密 原文: http://www.cnblogs.com/darkpig/p/5676076.html 第三方Crypto包提供了较全面的加密算法, ...
- latex math
scalar -> lower case lettervector -> lower case bold lettermatrix -> upper case bold letter
- JS常用的方法总结
/** * 将参数格式化为对象 * @param urlParams type string * @example * let urlParams = 'name="xiaoliu" ...
- VBA 字符串操作
Trim(string) 去掉string左右两端空白 Ltrim(string) 去掉string左端空白 Rtrim(string) 去掉string右端空白 Len(string) 计算stri ...
- setTimeout里无法调用鼠标事件的event
问题的由来是下面这段代码: middleOnmouseLeave: function (event) { setTimeout(function () { event.currentTarget.st ...
- 关于dll的pdb文件的小贴士
.pdb文件最好与生成它的.dll文件放在一起,这样调试的时候才有可能跟踪进dll的内部函数里.
- Disable Client Certificate Revocation (CRL) Check on IIS
Disable Client Certificate Revocation (CRL) Check on IIS: REGISTRY : HKLM\SYSTEM\CurrentControlSet\ ...
- Sleep Buddies
Sleep Buddies time limit per test 2.0 s memory limit per test 256 MB input standard input output sta ...