是否保证外部 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]))
所以真的不用自己动手,反正编译器会搞定的
请记住,checked
和 unchecked
会更改编译器发出的 指令 。例如。 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" 加法运算符取决于它是出现在选中还是未选中的上下文中。运算符影响程序执行的唯一方式是选择哪些运算符绑定到诸如“+”之类的标记;这种绑定在编译器检查代码时发生——而不是在 运行.
时发生
假设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]))
所以真的不用自己动手,反正编译器会搞定的
请记住,checked
和 unchecked
会更改编译器发出的 指令 。例如。 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" 加法运算符取决于它是出现在选中还是未选中的上下文中。运算符影响程序执行的唯一方式是选择哪些运算符绑定到诸如“+”之类的标记;这种绑定在编译器检查代码时发生——而不是在 运行.
时发生