[CLR via C#]5.1 基元类型

原文:[CLR via C#]5.1 基元类型

  某些数据类在开发中非常常用,以至于许多编译器允许代码已简化的语法来操作它们。例如可以使用以下语法来分配一个整数: 

System.Int32 a = new System.Int32();

  当然,你肯定不愿意使用这种语法,C#允许使用如下所示的语法:   

int a = 0;

  这种语法不仅增强代码的可读性,而且生成的IL代码和是有System.Int32时生成的IL代码是完全一致的。

  编译器直接支持的数据类型称为基元类型(primitive type)。基元类型直接映射到Framework类库(FCL)中存在的类型。比如以下4行代码都是正确的,生成的IL代码也是相同的。
int a = 0;
System.Int32 a =0;
int a = new int();
System.Int32 a = new System.Inte32();

  下表列出了FCL类型在C#中对应的基元类型:

C#基元类型 FCL类型 CLS相容 说明
sbyte System.SByte NO 有符号8位值
byte System.Byte YES 无符号8位值
short System.Int16 YES 有符号16位值
ushort System.UInt16 NO 无符号16位值
int System.Int32 YES 有符号32位值
uint System.UInt32 NO 无符号32位值
long System.Int64 YES 有符号64位值
ulong System.UInt64 NO 无符号64位值
char System.Char YES 16位Unicode字符
float System.Single YES IEEE32位浮点值
double System.Double YES IEEE64位浮点值
bool System.Boolean YES 一个true/false值
decimal System.Decimal YES 一个128位高精度浮点值,常用于不容许舍入误差的金融计算
string System.String YES 一个字符数组
object System.Object YES 所有类型的基类型
dynamic System.Object YES 对于CLR,dynamic和object完全一致。然而,C#编译器允许使用一个简单的语法,让dynamic变量参与动态调度

  可以想象C#编译器自动假定在所有的源代码文件中添加了以下using指令:  

using sbyte = System.Sbyte;
using byte = System.Byte;
using int = System.Int32;
using uint = System.UInt32;
......

  C#语言规范上说:"从风格上上,最好使用关键字,而不是使用完整的系统类型名称"。但本书作者并不同意这种说法,以下是他的一些理由:

  1)很多开发人员困惑于应该使用string还是String。由于C#的string(关键字)是直接映射到System.String(一个FCL类型),所以两者是没有区别的。还有开发人员认为,在32位系统中int是32位整数,在64位系统中就变成64位整数了,事实并不是这样。在C#中,int始终映射到System.Inte32,所有不管什么系统,int都是32位整数,如果都使用Int32就不会产生这种误解。

  2)在C#中,long映射到System.Int64,当在其他编程语言中,long可能映射到的是Int16或Int32。这样在看别的编程语言时容易产生误解。

  3)FCL的许多方法都将类型名称作为方法名的一部分。

 
BinaryReader br =new BinaryReader(...);

        float val = br.ReadSingle();      //正确,当看上去不自然

        Single val = br.ReadSingle();    //正确,看上去一目了然
   
  4)平时只用C#的许多程序员逐渐淡忘了还可以使用其他语言写面向CLR的代码。因此造成了"C#主义"入侵类库代码。
  
  对基元类型执行许多算数运算都可能造成溢出。不同语言处理溢出也是不同的。C和C++不将溢出视为错误,并允许值回滚;应用程序"若无其事"的运行着。相反,Microsoft Visual Basic总是将溢出视为错误,并会抛出异常。
 
  CLR提供了一些特殊的IL指令,允许编译器选择它认为最恰当的行为。CLR有一个add指令,将两值相加但不检查溢出。还有一个add.ovf指令,作用是两值相加,溢出时抛出异常。类似的还有sub/sub.ovf等。
 
  C#允许开发人员自己决定如何处理溢出。溢出检查默认是关闭的。开发人员可以使用C#编译器控制溢出的一个办法是使用/checked+编译器开关。
 
  C#通过提供checked和unchecked操作符来实现局部是否检查发生溢出。
unchecked:
UInt32 invalid = unchecked((UInt32)(-1)); //OK
checked:
Byte b = 100;
b = checked((Byte)(n+200)); //抛出溢出异常

  C#还提供checked和unchecked语句

checked{
Byte b = 100;
b = checked((Byte)(n+200));
}

  在Visaul Studio的"高级生成设置"对话框中可以指定编译器是否检查溢出。

  System.Decimal类型是一个非常特殊的类型。虽然C#将Decimal视为一个基元类型,但CLR则不然,也就是说CLR没有相应的IL指令来决定如何处理Decimal值。Decimal值得处理速度是要慢于其他CLR基元类型的值得处理速度。还有对Decimal来说,checked和uncheked操作符、语句和编译器都是无效的,Decimal溢出时是一定会抛出异常的。

上一篇:iOS面试2


下一篇:java获取任意日期