33

也许我是基础知识,但我仍在学校学习这个 C# 东西。我知道,如果我将 1 加到最大值 Integer,即 32 位,结果将为负数。我读到 C# 提供了检查和未检查的关键字来处理溢出。Checked 关键字是一些东西,我发现它很有用,但是 unchecked 关键字呢?我真的找不到 unchecked -keyworded 块没有多大用处。有没有?接下来的两种方法有何不同?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Practice_6
{
    class Program
    {
        static void Main(string[] args)
        {
            int value = Int32.MaxValue;
            value++;
            //Approach 1 to make a decision
            if (value > Int32.MaxValue) {
                //Do something
            }
            value = Int32.MaxValue;
            //Approach 2 to make a decision
            unchecked {
                value++;
                //Do something
            }
            //What's the difference between these two approaches to handle overflow?
    }
}
4

5 回答 5

50

更新:这个问题是我 2015 年 4 月博客的主题;谢谢你的有趣问题!


你的第一个问题是:

checked关键字很有用,但是关键字unchecked呢?我真的找不到它的用途。有没有?

C# 设计团队没有添加对语言没有用的特性的习惯。(除了一元加号运算符,世界上最没用的运算符。)

unchecked 关键字有两个主要用例。

首先,默认情况下始终检查常量整数算术。这可能很烦人。例如,假设您有一些互操作代码,并且您希望为 HRESULT E_FAIL 创建一个常量:

const int E_FAIL = 0x80004005;

这是一个错误,因为该数字太大而无法放入int. 但是您可能不想使用uint. 你可能想得好我就说

const int E_FAIL = (int)0x80004005;

但这也是非法的,因为默认情况下总是检查包括转换在内的常量算术。

你需要做的是通过关闭检查常量算术

const int E_FAIL = unchecked((int)0x80004005);

其次,C# 中非常量整数数学的默认算术是未选中的,因为它更快,并且因为这是许多其他类似语言所做的。但是,C# 允许您通过编译器标志将非常数整数数学的默认值更改为检查算术。如果您已经这样做了,并且您需要暂时关闭它,那么您必须使用unchecked块或表达式语法。

第三个用例是将未检查的块用作一种自我记录代码的形式,表示“我知道我在这里执行的操作可能会溢出,这对我来说很好。” 例如,我经常会写这样的东西:

int GetHashCode()
{
    unchecked 
    {
        int fooCode = this.foo == null ? 0 : this.foo.GetHashCode();
        int barCode = this.bar == null ? 0 : this.bar.GetHashCode();
        return fooCode + 17 * barCode;
    }
}

“未检查”向读者强调,我们完全期望哈希码相乘和相加可能会溢出,这是可以的。

你的第二个问题是:

这两种处理溢出的方法有什么区别?

好问题。

如果在您检查的上下文中您的意思是:我希望算术始终在界限内;如果不是,那么我的程序有一个严重的错误,必须在它对世界造成更多伤害之前终止,那么你根本不应该进行任何溢出检查,因为没有溢出。任何异常都应该终止程序;检查的上下文只是使运行时工作以验证代码的正确性。

如果在您检查的上下文中,您的意思是我需要算术在范围内,但我从不可靠的来源获得了这些数据,我懒得对其进行范围检查,那么您应该捕获异常。 但是,更好的做法是进行范围检查并在数据超出范围时给出更有意义的错误,而不是让运行时注意到问题并引发溢出异常。我强烈建议进行范围检查而不是捕获异常;不要偷懒。

于 2014-09-18T16:00:43.947 回答
5

我不太确定checkedand的性能影响unchecked。理论上,unchecked应该更高效,并且它是默认上下文。除非您为某种特殊算法/业务逻辑等围绕整数类型的边界徘徊,否则checked很少需要/有用/可读。

正如我所说,unchecked是默认上下文,那么为什么需要unchecked关键字?一件事可能是明确说明上下文使用率很高的checked上下文类型。另一种是在上下文中使用unchecked上下文checked

checked {
    int a = 5 + 1231;

    unchecked {
        a += 221;
    }
}

您的问题可能是为什么未选中默认上下文。我想这是微软的设计选择。

它们的区别在于checked上下文检查每个算术运算是否存在溢出,如果存在溢出则引发异常。

于 2014-09-11T03:40:09.187 回答
4

不应期望正确编写的代码取决于项目是在编译时使用选中还是未选中作为默认值。意外的包装算术行为可能对安全性或系统稳定性造成风险的代码应在checked上下文中执行。依赖于包装算术行为的代码应该在unchecked上下文中完成。预计不会发生算术溢出的通用代码,但如果发生的话后果有限,应使用项目级设置进行编译。然后可以将项目级别设置设置为选中或未选中作为执行速度和及时错误捕获之间的权衡。

如果任何地方都没有发生意外溢出,代码将在未检查模式下运行得更快。但是,如果某些计算产生虚假值并且不清楚原因,则启用溢出检查的重新编译可能会有所帮助。它会导致代码运行得更慢,但会在第一次错误计算时陷入困境。但是,仅当依赖于包装算术行为的项目部分在显式unchecked上下文中执行时,才能将项目切换到检查模式以实现此目的。否则切换项目级模式只会完全破坏程序。

于 2014-09-18T16:45:46.237 回答
2

从 MSDN 使用 unchecked 的原因如下

因为检查溢出需要时间,所以在没有溢出危险的情况下使用未经检查的代码可能会提高性能。但是,如果可能发生溢出,则应使用经过检查的环境。

于 2014-09-11T03:40:21.233 回答
0

将您的代码更改为:

  int value = int.MaxValue;
  unchecked
  {
    value++;
  }
  Console.WriteLine(value);
  value = int.MaxValue;
  checked
  {
    value++; // this will raise OverflowException
  }

你会看到区别。

于 2014-09-11T03:40:22.587 回答