checked 和 unchecked 语句

控制相应块中的整型数算术运算和转换的溢出检查,checked 为检查,unchecked 为不检查。

checked 和 unchecked 运算符

控制相应表达式中的整型数算术运算和转换的溢出检查,checked 为检查,unchecked 为不检查。
checked 和 unchecked 语句与运算符仅仅是作用区域不同,效果一致。

溢出检查与不检查

用最简单的 byte 类型描述两者的区别。checked 可以在 unchecked 代码段或表达式(溢出不检查)中指定某一部分强制检查。

备注
C# 的 byte 没有加法(既没有 + 运算符,也没有 add 方法)。下例中只能强制转换加法结果,否则就被作为 int 加法,出现警告。byte b = a + z3; 或者 byte b = ( byte ) a + ( byte ) z3; 均警告 CS0266:无法将类型“int”隐式转换为“byte”
byte a = byte . MaxValue;
byte z3 = 3;

checked
    {
        try
            {
                byte b = ( byte )( a + z3 );
                Console . WriteLine ( b );
            }
        catch ( OverflowException ych溢出 )
            {
                Console . WriteLine ( ych溢出 . Message );
            }
    }

上例指定的该代码块溢出检查。由于 byte 无法表示大于 255 的整数(255 + 3),所以得到结果:

Arithmetic operation resulted in an overflow.

那么溢出不检查呢?

byte a = byte . MaxValue;
byte z3 = 3;

unchecked
    {
        byte b = ( byte )( a + z3 );
        Console . WriteLine ( b );
    }

上例指定该代码块不检查溢出。于是结果很奇怪:2。
上例中的结果来自下式(2 进制的):

$$ 11111111 + 00000011 = (1)00000010(括号内抛弃) $$

unchecked 强制进行运算,但抛弃溢出目标类型的二进制位,只返回目标范围内的二进制位。checked 只会引发异常,或顺利运行。

是否检查溢出的用途

以下操作受 checked 和 unchecked 的操作符和语句建立的溢出检查上下文的影响:

  • 当操作数为整型或枚举类型时,预定义的 ++ 和 -- 操作符。
  • 当操作数为整型时,预定义的一元操作符 -。
  • 当两个操作数都是整型或枚举类型时,预定义的加减乘除二元操作符。
  • 从一个整型或枚举类型到另一个整型或枚举类型,或从浮点型或双精度型到整型或枚举类型的显式数值转换。

当上述操作之一产生的结果太大而无法在目标类型中表示时,执行该操作的上下文控制结果行为:

  • 在 checked 上下文中,如果操作是常量表达式,则会发生编译时错误。否则,当在运行时执行该操作时,会出现 System . OverflowException。
  • 在 unchecked 上下文中,通过丢弃不适合目标类型的任何高阶位来截断结果。

    备注
    对于非常量表达式(在运行时求值的表达式),没有处于任何 checked 或 unchecked 块或函数中,默认的溢出检查上下文是不检查的,除非外部因素(如编译器开关和执行环境配置)要求检查。
    对于常量表达式(可以在编译时完全求值的表达式),总是默认检查溢出上下文。除非将常量表达式显式地放置在未检查的上下文中,否则在表达式的编译时求值期间发生的溢出总是会导致编译时错误。
  • 匿名函数不受发生匿名函数的 checked 或 unchecked 上下文的影响。

下例三个函数,三个结果不同哦:

static readonly int x = 1000000;
static readonly int y = 1000000;

static int c ( ) => checked(x * y); // 引发 OverflowException
static int u ( ) => unchecked(x * y); // 返回 -7,273,379,968
static int m ( ) => x * y; // 引发异常或返回值(根据是否在 checked 上下文中返回结果)

checked 和 unchecked 运算符只会影响文本包含在“(”和“)”标记中的运算的溢出检查上下文。 运算符对函数成员没有影响,这些函数成员是在对所含表达式进行求值后被调用的。

以下代码中:

{
    static int Multiply(int x, int y) => x * y;
    static int F() => checked(Multiply(1000000, 1000000));
}

在 F 中使用 checked 不会影响 Multiply 中 x y 的求值,因此 x y 将在默认的溢出检查上下文中求值。

方便之处

当以十六进制表示法编写带符号整型类型的常量时,unchecked 运算符很方便。

{
    public const int AllBits = unchecked ( ( int ) 0xFFFFFFFF );
    public const int HighBit = unchecked ( ( int ) 0x80000000 );
}

上述两个十六进制常量的类型都是 uint。 由于常量超出了 int 的范围,如果没有 unchecked 运算符,将常量强制转换为 int 会产生编译时错误。

注释:
checked 和 unchecked 运算符和语句允许程序员控制某些数值计算的某些方面。 但是,某些数值运算符的行为取决于其操作数的数据类型。 例如,两个小数相乘总是会在溢出时出现异常,即使在显式未检查的结构中也是如此。 同样,即使在显式检查的结构中,两个浮点数相乘也不会出现溢出异常。 此外,其他运算符永远不会受到检查模式的影响,无论是默认还是显式。永远不要对 float 和 double 进行 checked 和 unchecked。

兔子码农
2 声望1 粉丝

一个酒晕子


« 上一篇
C# 的 char