C# checked和unchecked详解
1、对基元类型执行的许多算术运算都可能造成溢出,有如下代码:
Byte b=100; b=(Byte)(b+200);
简单的解读上面的代码:
第一步,将所有的操作数都扩大至32位或者64位(根据操作系统的位数决定)。所以b和200(这两个值都不超过32位),首先转换成32位(假设当前操作系统是32位),然后加到一起。结果就是一个32位值(十进制300或者十六进制12C)。注意此时的值为一个32位的操作数,必须转型为一个byte。C#不会隐式地执行这个转型操作,这正是第二行代码需要强制转换为Byte的原因.如果不把结果值强制转换为Byte,代码如下:
byte b = 100;
b = b +300;
如果将值强行转换为Byte,那么还会出现一个问题,就是值溢出的问题,Byte只能表示0~255范围的值,所以300超出了Byte的范围,值就溢出了.不同的语言以不同的方式处理溢出,C和C++不视溢出为错误,并允许值回滚.应用程序将若无其事的运行.相反,Microsoft Visual Basic总将溢出视为错误,并会在检测到溢出时抛出一个错误.
而CLR提供了一些特殊的IL指令,允许编译器选择它认为最正确的行为。CLR有一个add指令,将作用是将两个值加到一起,但不执行溢出检查。CLR还有一个add.ovf的指令,作用是将两个值加到一起,但会在抛出异常时抛出一个System.OverflowException异常。除了用于加法运算的这两个IL指令外,CLR还为减、乘和数据转换提供了类似的IL指令,分别是sub/sub.ovf,sub/sub.ovf和conv/conv.ovf。
也就是说C#允许程序员自己决定如何处理溢出,溢出检查默认是关闭的。因为这样能保证代码的运行效率,但是开发人员必须保证不会发生溢出,或者他们的代码能预见到这些溢出.
2、控制溢出的方法
第一种:打开/checked编译器开关.这个开关指示编译器在生成代码时,使用加、减、乘、除和转换指令的溢出检查版本也就是带.vof的版本,这样,在生成代码时,就会检查代码是否溢出.
下面是/checked编译器开关的打开方式:
第二种:就是用checked和unchecked关键字来控制溢出的检查与否,这体现的C#溢出检查的灵活性.
下面是一个在/checked编译器开关打开的情况下,使用unchecked关键字强制不检查unchecked包裹的代码的溢出问题,代码如下:
UInt32 a = unchecked((UInt32)(-1));
Console.WriteLine(a); //一个很大的数
下面在/checked编译器开关关闭的情况下,使用checked关键字检查其包裹的代码的溢出问题,代码如下:
byte b = 100;
b =checked((Byte)(b +300)); //溢出错误
Console.WriteLine(b);
3、checked和unchecked语句
除了上面的checked和unchecked关键字外,checked和unchecked还可以是语句,它们造成一个块中的表达式就进行/不进行溢出检查.代码如下:
checked
{
byte b = 100;
b += 200;//在checked语句块内,可以直接使用+=操作符,编译器自动会把值转换为byte,前提200必须在byte范围内
Console.WriteLine(b);
}
4、关于基元类型进行算术操作产生溢出的建议
a、在应用程序能够容忍checked运算造成的性能损失的情况下,尽可能的打开/checked编译器开关,保证程序的正常运行
b、尽量使用有符号整数(Int32,Int64),少使用无符号整数(UInt32,UInt64)
c、将不希望发生overflowException的代码块作用于checked关键字下,并捕获overflowException,并即时从异常中恢复.
d、c的反例,unchecked的用法.
相关文章
- C#中的泛型详解
- C#基础知识详解之【字段与属性】
- 《C#高级编程》学习笔记----c#内存管理--栈VS堆
- c#的DateTime.Now函数详解
- C#进阶系列——WebApi 接口参数不再困惑:传参详解
- C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解
- C#删除字符串最后一个字符的几种方法
- C# 视频监控系列(12):H264播放器——播放录像文件
- c# 优化代码的一些规则——const 和 readonly[二]
- C# FTP 上传 下载(汇总)
- C# Protobuf-Net 序列化
- C# Table.Rows[index].Delete()
- C# Socket 线程
- C#语法糖
- C# 容器上控件排序
- C# 判断DATASET是否为空
- C# 反射详解
- Atitit. c# 语法新特性 c#2.0 3.0 4.0 4.5 5.0 6.0 attilax总结 1. 版本历史1 1.1. C# 1.0-纯粹的面向对象2 1.2. C# 2.0
- C# static的用法详解
- C# ORM—Entity Framework 之Code first(代码优先)(二)
- C# winform坐标系类型详解
- C# 泛型详解
- C#窗体间常用的几种传值方式及委托与事件详解
- C#实现水晶报表绑定数据并实现打印3-二维码条形码
- C#多线程_Thread.Join()
- C#中abstract的用法详解
- C# 获取C++内存
- C# 序列化和反序列化 详解
- 利用C#操作WMI指南
- C# AppDomain 详解
- [C#]c#中数据的同步加锁机制 的几种方法
- VS2019下C#调用C++ DLL详解+数据转换
- 【UiPath2022+C#】UiPath变量和参数