是否保证外部 checked/unchecked 上下文会影响在其中创建的 lambda 的行为?

Is it guaranteed that outer checked/unchecked context influences the behavior of lambdas created inside it?

假设default算术溢出(不)校验,如下代码

Action<Int32[]> action;

checked
{
    action = array =>
        Console.WriteLine(array[0] + array[1]);
}

var items = new[]
{
    Int32.MaxValue,
    Int32.MaxValue
};
action(items);

将导致

System.OverflowException: Arithmetic operation resulted in an overflow..

如果我们将项目设置设置为/checked,并将checked {替换为unchecked {,则不会抛出异常。

那么,我们可以依靠这种行为,还是 array => unchecked (array[0] + array[1]) 更安全?

在最近正式发布的 C# 规范中,它是这样说的:

8.11 (...) The checked statement causes all expressions in the block to be evaluated in a checked context, and the unchecked statement causes all expressions in the block to be evaluated in an unchecked context. (...)

我会非常自信地说 action 将始终在已检查/未检查的上下文中进行评估,这是您当前看到的行为,我不希望这种情况在未来发生变化。

为了进一步扩展我的回答,如果您检查编译后的代码,您会发现 checked 语句中的 Console.WriteLine(array[0] + array[1]) 实际上编译为 Console.WriteLine(checked (array[0] + array[1])) 所以真的不用自己动手,反正编译器会搞定的

请记住,checkedunchecked 会更改编译器发出的 指令 。例如。 IL 中的 add 指令有两种(实际上更多)变体,其中一种变体 ignores 溢出,另一种 checks 溢出.

因为它改变了发出的 IL,所以它必须应用。


例如此代码:

    static void Main(string[] args)
    {
        int i = 0;
        int j = 1;
        int k;
        checked
        {
            k = i + j;
        }
        unchecked
        {
            k = i + j;
        }
        Console.ReadLine();
    }

发出此 IL:

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 2
    .locals init (
        [0] int32 num,
        [1] int32 num2,
        [2] int32 num3)
    L_0000: nop 
    L_0001: ldc.i4.0 
    L_0002: stloc.0 
    L_0003: ldc.i4.1 
    L_0004: stloc.1 
    L_0005: nop 
    L_0006: ldloc.0 
    L_0007: ldloc.1 

    L_0008: add.ovf 

    L_0009: stloc.2 
    L_000a: nop 
    L_000b: nop 
    L_000c: ldloc.0 
    L_000d: ldloc.1 

    L_000e: add 

    L_000f: stloc.2 
    L_0010: nop 
    L_0011: call string [mscorlib]System.Console::ReadLine()
    L_0016: pop 
    L_0017: ret 
}

在哪里可以看到发出的两条不同的指令。

最好将 C# 视为具有两组整数运算符,其中一组执行溢出检查,而另一组不执行; “+”运算符是绑定到 "overflow-checked addition" 运算符还是绑定到 "wrapping addition" 加法运算符取决于它是出现在选中还是未选中的上下文中。运算符影响程序执行的唯一方式是选择哪些运算符绑定到诸如“+”之类的标记;这种绑定在编译器检查代码时发生——而不是在 运行.

时发生