我应该避免在C#中使用unsigned int吗?


23

我最近想到了在C#中使用无符号整数(并且我猜可以对其他“高级语言”说类似的论点)

当需要整数时,我通常不会遇到整数大小的难题,例如Person类的age属性(但问题不限于属性)。考虑到这一点,据我所知,使用无符号整数(“ uint”)优于有符号整数(“ int”)的唯一优势-可读性。如果我想表达年龄只能为正数的想法,可以通过将年龄类型设置为uint来实现。

另一方面,对无符号整数的计算可能会导致各种错误,并且使其难以执行诸如减去两个年龄的运算。(我读到这是Java省略无符号整数的原因之一)

在C#的情况下,我还可以认为setter上的保护子句可以提供两个世界中最好的解决方案,但是,例如当我将年龄传递给某种方法时,这将不适用。一种解决方法是定义一个名为Age的类,并将属性age作为唯一的对象,但是这种模式将让Me创建许多类,并且会造成混乱(其他开发人员将不知道什么时候对象只是包装器)以及更复杂的内容)。

关于此问题的一些最佳常规做法是什么?我应该如何处理这种情况?



1
此外,unsigned int不符合CLS,这意味着您不能从其他.NET语言中调用使用它们的API。
内森·库珀

2
@NathanCooper:...“无法从其他某些语言调用使用它们的API ”。它们的元数据是标准化的,因此所有支持无符号类型的.NET语言都可以很好地互操作。
Ben Voigt

5
为了解决您的特定示例,我首先没有一个名为Age的属性。我将拥有一个名为Birthday或CreationTime或其他属性,并从中计算年龄。
埃里克·利珀特

2
“……但是这种模式会让我创建许多类,并且会引起混乱”,实际上这是正确的做法。只需搜索臭名昭著的原始痴迷反模式。
Songo'1

Answers:


24

.NET Framework的设计人员选择32位带符号整数作为其“通用数字”是出于以下几个原因:

  1. 它可以处理负数,尤其是-1(框架用来指示错误情况;这就是为什么在需要索引的地方都使用带符号的int的原因,即使负数在索引上下文中没有意义)。
  2. 它足够大,可以满足大多数目的,同时又足够小,几乎可以在任何地方经济使用。

使用无符号整数的原因不是可读性。它具有获得仅unsigned int提供的数学功能。

保护条款,验证和合同前提条件是确保有效数字范围的完全可接受的方法。现实世界中的数字范围很少与0到2 32 -1 之间的数字完全对应(或本机数字范围是您选择的数字类型),因此使用a uint将接口协定约束为正数是一种除此之外。


2
好答案!在某些情况下,无符号int可能实际上无意间产生了更多错误(尽管可能立即发现了一些错误,但有点令人困惑)-想象一下用无符号int计数器反向循环,因为某些大小是整数:for (uint j=some_size-1; j >= 0; --j)-糟糕(不知道这是否是C#中的问题)!我在代码中发现了这个问题,在此之前,尝试在C端尽可能多地使用unsigned int -我们最终将其更改为int以后才更喜欢,而且使用更少的编译器警告也使我们的工作变得更加轻松。

14
“现实世界中的数字范围很少与0到2 ^ 32-1之间的数字相对应。” 以我的经验,如果您需要一个大于2 ^ 31的数字,那么最终您还很可能需要大于2 ^ 32的数字,因此您最好将其移至(signed)int64那一点。
梅森惠勒

3
@Panzercrisis:有点严重。说“ int大多数时候使用,因为这是已建立的约定,这是大多数人希望经常使用的习惯。uint当您需要特殊的Capabilites时使用”,这可能会更准确uint。记住,Framework设计者决定广泛遵循此约定,因此您甚至不能uint在许多Framework上下文中使用(它与类型不兼容)。
罗伯特·哈维

2
@Panzercrisis这可能是一个过于强烈的措辞;但是我不确定是否曾经在C#中使用过无符号类型,除非当我调用win32 api时(约定是常量/标志/无符号)。
Dan Neely

4
确实的确如此。我唯一一次使用无符号整数的情况是在位混乱的情况下。
罗伯特·哈维

8

通常,应始终对数据使用最特定的数据类型。

例如,如果您正在使用Entity Framework从数据库中提取数据,则EF将自动使用最接近数据库中使用的数据类型。

在C#中有两个问题。
首先,大多数C#开发人员仅使用int来表示整数(除非有使用的理由long)。这意味着其他开发人员将不会考虑检查数据类型,因此他们将获得上述的溢出错误。第二,更重要的问题,是/是.NET的原始算术运算符仅支持intuintlongulongfloat,双,和decimal*。如今仍然如此(请参见C#5.0语言规范中的 7.8.4节)。您可以使用以下代码自己进行测试:

byte a, b;
a = 1;
b = 2;
var c = a - b;      //In visual studio, hover over "var" and the tip will indicate the data type, or you can get the value from cName below.
string cName = c.GetType().Namespace + '.' + c.GetType().Name;

我们的结果byte- byteintSystem.Int32)。

这两个问题引起了非常普遍的“仅将整数用于整数”的实践。

因此,要回答您的问题,在C#中通常要坚持使用,int除非:

  • 自动代码生成器使用其他值(例如Entity Framework)。
  • 该项目上的所有其他开发人员都知道您正在使用不太常见的数据类型(包括一条注释,指出您使用了该数据类型以及原因)。
  • 不太常见的数据类型已经在项目中普遍使用。
  • 该程序需要使用较少见的数据类型的好处(您有1亿个数据需要保存在RAM中,因此a byte和an int或an int和a 之间的区别long很关键,或者已经提到的unsigned的算术区别)。

如果您需要对数据进行数学运算,请遵循常见的类型。
请记住,您可以从一种类型转换为另一种类型。从CPU的角度来看,这可能会降低效率,因此使用7种常见类型中的一种可能会更好,但是如果需要的话,它是一个选择。

列举(enum)是上述准则的我个人例外之一。如果我只有几个选项,则将枚举指定为字节或短整数。如果需要标记的枚举中的最后一位,则将类型指定为,uint以便可以使用十六进制设置标记的值。

如果确实使用带有值限制代码的属性,请确保在摘要标签中说明存在哪些限制以及原因。

* C#别名用于代替.NET名称,System.Int32因为这是一个C#问题。

注意:.NET开发人员有一篇博客或文章(我找不到),该博客或文章指出了算术函数的数量有限以及他们不担心它的一些原因。我记得,他们表示他们没有计划增加对其他数据类型的支持。

注意:Java不支持无符号数据类型,并且以前不支持8或16位整数。由于许多C#开发人员来自Java背景或需要同时使用两种语言,因此有时会人为地将一种语言的限制强加给另一种语言。


我的一般经验法则很简单,“除非不能,否则请使用int”。
PerryC '16

@PerryC我相信这是最常见的约定。我的回答的重点是提供一个更完整的约定,使您可以使用语言功能。
Trisped

6

您主要需要注意两件事:您所代表的数据以及计算中的任何中间步骤。

年龄一定是合理的unsigned int,因为我们通常不考虑负面年龄。但是随后您提到从另一个年龄中减去一个年龄。如果我们只是盲目地从另一个整数中减去一个整数,那么即使我们先前同意负年龄没有意义,也肯定有可能以负数结尾。因此,在这种情况下,您希望使用带符号整数完成计算。

关于无符号值是否不好,我想说无符号值不好是一个很大的概括。正如您所提到的,Java没有无符号的值,并且它一直困扰着我。A byte的值可以在0-255或0x00-0xFF之间。但是,如果要实例化一个大于127(0x7F)的字节,则必须将其写为负数或将整数转换为字节。您最终得到的代码如下所示:

byte a = 0x80; // Won't compile!
byte b = (byte) 0x80;
byte c = -128; // Equal to b

以上让我无休止。我不允许一个字节的值为197,即使对于大多数理智的人来说,这是一个非常有效的值。我可以转换整数,也可以找到负值(在这种情况下为197 == -59)。另外考虑一下:

byte a = 70;
byte b = 80;
byte c = a + b; // c == -106

如您所见,将两个具有有效值的字节相加,然后以具有有效值的字节结尾,最终将改变符号。不仅如此,还不是很明显70 + 80 == -106。从技术上讲,这是一个溢出,但是在我看来(作为人类)对于0xFF以下的值,字节不应溢出。当我在纸上进行位算术运算时,我不认为第8位是符号位。

我在位级别上使用很多整数,对所有符号进行签名通常会使所有内容变得不那么直观,也更难处理,因为您必须记住,右移负数会为您带来新1的数字。右移无符号整数绝不会这样做。例如:

signed byte b = 0b10000000;
b = b >> 1; // b == 0b1100 0000
b = b & 0x7F;// b == 0b0100 0000

unsigned byte b = 0b10000000;
b = b >> 1; // b == 0b0100 0000;

它只是增加了我认为不必要的额外步骤。

我在byte上面使用时,同样适用于32位和64位整数。没有unsigned这种语言会给人留下深刻的印象,这让我震惊,因为像Java这样的高级语言根本不允许使用它们。但是对于大多数人来说,这不是问题,因为许多程序员不处理位级算术。

最后,如果将无符号整数视为位,则使用无符号整数是有用的;而当将它们视为数字时,则使用有符号整数将很有用。


7
我对没有无符号整数类型(尤其是字节)的语言感到沮丧,但恐怕这不是这里所提问题的直接答案。也许您可以添加一个结论,我相信可能是:“如果您将无符号整数考虑为位,则使用无符号整数;如果您将数字视为数字,则应使用有符号整数。”
5gon12eder,2016年

1
这就是我在上面的评论中所说的。很高兴看到其他人以同样的方式思考。
罗伯特·布里斯托
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.