一,基元类型的运算
在基元类型的运算方面,C#编译器所提供的最大的特性就是,能够根据用户的设置进行溢出检查,当产生溢出时CLR抛出一个异常,这样用户就可以检查并处理这个异常。在默认情况下,编译器是不会进行溢出检查的,如下:
private static void AddTwoInteger()
{
int a = int.MaxValue;
int b = 12;
long c = a + b;
}
根据直觉,这段代码中,c会等于2147483659(2147483647+12);但事实c却等于-2147483648。
a+b运算产生了溢出,但是在运行的时候却没有抛出异常,这不是我想要的。那么如何让这段代码在运行的时候抛出一个异常呢,这可以通过使用checked来检查溢出,当checked内的运算发生了溢出,checked就会抛出一个异常。将代码修改如下:
private static void AddTwoInteger()
{
checked
{
int a = int.MaxValue;
int b = 12;
long c = a + b;
}
}
从新编译这段代码,就会在运行的时候抛出一个overflowException异常。然后可以捕获这个异常,采取处理。
那么为什么加上一个checked之后 ,就会在运算产生溢出时抛出一个异常呢?通过查看IL代码可以找到答案。
在用checked修饰的那段代码中,编译器为a+b运算生成了一个add.ovf指令,而这个指令最大的特点就是在执行加法运算的时候,如果结果太大,也就是产生了溢出时会抛出一个OverflowException异常。
而第一段代码中,由于没有使用check,所以编译器就生成一个add指令,这个指令在结果产生溢出的时候不会抛出一个异常。
都有哪些运算可能产生溢出?
1.加法运算可能会产生溢出
2.加法运算可能会产生溢出
2.乘法运算可能会产生溢出
3.将一个宽数据转为窄数据时可能会产生溢出(譬如将Int64转为Int32)
如何在实际开发中使用溢出检查?
由于溢出检查会带来性能损失,因为在溢出的时候会抛出异常。因此在release版中是不太会使用溢出检查的,一般都是在Debug版中进行溢出检查,原因很简单,就是便于调试,试想一想如果不使用溢出检查,当代码中出现溢出运算时,程序依旧会悄然无声的运行,就像什么都没有发生一样,对于这样的操作找出错误的发生很是困难。
为了使用溢出检查,会在代码中大量的嵌入checked,然后还要在发布版本去掉他们。还有一些更简单的方法,那就是使用csc命令行工具编译源代码时使用checked参数,如下:
在debug中使用上述的checked+命令行编译,就会在生成的IL指令中使用类似于add.ovf这样带溢出检查的指令。我们完全不用在源代码中使用checked关键咯。当要生成release版时,则使用checked-,就不会使用溢出检查了。
事实上,正如开始所说的,编译器默认是不进行溢出检查的。所以上述命令行完全可以去掉checked-参数。
二,直接量
由于编译器对直接量的支持,所以下面代码能够正常编译:
string a = 1.ToString();
可以看出,对于直接量的支持,完全是为了简便用户的一些输入而已。譬如如果编译器不支持直接量,那么要想将整形2转换为string,则必须要这样写:
Int32 x=2;
string a = x.ToString();
三,操作符的支持
同样,编译器提供操作符也只是为了简化用户输入而已,并使代码更符合我们的直觉,如下:
int a=2;
int b=3;
int c=a+b
使用+来进行加法运算时再自然不过的了,所以提供操作重载是很有好处的。但这并不是必要的。