C#中未选中的-keyword

时间:2014-09-11 03:27:21

标签: c# unchecked-conversion

也许我已经掌握了基础知识,但我仍然在学校学习这个C#的东西。据我所知,如果我将1加到最大值整数,哪一个是32位,结果将是负数。我读到C#提供了检查和未检查的关键字来处理溢出。已检查的关键字是一种东西,我发现它很有用,但未经检查的关键字怎么样?我真的找不到用于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?
    }
}

5 个答案:

答案 0 :(得分:39)

更新:这个问题是the subject of my blog in April 2015;感谢有趣的问题!


你的第一个问题是:

  

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;
    }
}

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

你的第二个问题是:

  

这两种处理溢出的方法之间的区别是什么?

好问题。

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

如果在你选中的上下文中你的意思是我要求算术在界限范围内但是我从一个不值得信任的来源获得这些数据并且我懒得对它进行范围检查然后你应该捕获异常。 然而,更好的做法是进行范围检查,如果数据超出范围则给出更有意义的错误,而不是让运行时注意到问题并抛出溢出异常。我强烈建议进行范围检查而不是捕捉异常;不要偷懒。

答案 1 :(得分:5)

我不太确定checkedunchecked对性能的影响。理论上,unchecked应该更高效,它是默认的上下文。除非你围绕某种特殊算法/业务逻辑等的整数类型的界限,否则使用checked很少是必要/有用/可读的。

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

checked {
    int a = 5 + 1231;

    unchecked {
        a += 221;
    }
}

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

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

答案 2 :(得分:4)

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

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

答案 3 :(得分:2)

从MSDN中使用未选中的原因如下

  

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

答案 4 :(得分:0)

将您的代码更改为:

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

你会发现差异。