没有强制转换的两个字节变量的C#XOR将无法编译

为什么以下引发编译时错误:’无法将类型’int’隐式转换为’byte’:

byte a = 25; byte b = 60; byte c = a ^ b; 

如果我使用算术运算符,这将是有意义的,因为a + b的结果可能大于可以存储在单个字节中的结果。

然而,将其应用于XOR运算符毫无意义。 这里是一个按位操作,它永远不会溢出一个字节。

在两个操作数周围使用一个转换器:

 byte c = (byte)(a ^ b); 

我不能给你理由,但我可以告诉为什么编译器从编译器必须遵循的规则的角度来看具有这种行为(这可能不是你知道的有趣之处)。

从C#规范的旧版本(我应该下载更新的版本),强调添加:

14.2.6.2二进制数字促销本条款内容丰富。

对于预定义的+ ,?的操作数,会发生二进制数字提升?*/%&|^==!=><>=<=二元运算符。 二进制数字促销隐式地将两个操作数转换为公共类型,在非关系运算符的情况下,它也成为操作的结果类型。 二进制数字促销包括按照它们在此处显示的顺序应用以下规则:

  • 如果任一操作数的类型为十进制,则另一个操作数将转换为十进制类型,否则如果另一个操作数的类型为float或double,则会发生编译时错误。
  • 否则,如果任一操作数的类型为double,则另一个操作数将转换为double类型。
  • 否则,如果任一操作数的类型为float,则另一个操作数将转换为float类型。
  • 否则,如果任一操作数的类型为ulong,则另一个操作数将转换为ulong类型,否则如果另一个操作数的类型为sbyte,short,int或long,则会发生编译时错误。
  • 否则,如果任一操作数的类型为long,则另一个操作数将转换为long类型。
  • 否则,如果任一操作数的类型为uint而另一个操作数的类型为sbyte,short或int,则两个操作数都将转换为long类型。
  • 否则,如果任一操作数的类型为uint,则另一个操作数将转换为类型uint。
  • 否则, 两个操作数都将转换为int类型

因此,基本上小于int操作数将转换为这些运算符的int (并且结果将是非关系运算的int )。

我说我不能给你一个理由; 但是,我会猜测一下 - 我认为C#的设计者希望确保在缩小时可能丢失信息的操作需要让程序员以强制转换的forms明确缩小操作。 例如:

 byte a = 200; byte b = 100; byte c = a + b; // value would be truncated 

虽然在两个字节操作数之间执行xor操作时不会发生这种截断,但我认为语言设计者可能不希望有一组更复杂的规则,其中某些操作需要显式转换而其他操作则不需要。


只是一个小小的注释:上面的引用是“信息性的”而不是“规范性的”,但它以易于阅读的forms涵盖了所有案例。 严格来说(在规范意义上), ^运算符以这种方式运行的原因是因为在处理byte操作数时该运算符的最近重载是(来自14.10.1“整数逻辑运算符”):

 int operator ^(int x, int y); 

因此,正如信息文本所解释的那样,操作数被提升为int并产生int结果。

微软的半神程序员有一个答案: http : //blogs.msdn.com/oldnewthing/archive/2004/03/10/87247.aspx

也许它更多的是关于编译器设计。 它们通过推广编译过程使编译器更简单,它不必查看操作数的运算符,因此它将按位运算与算术运算符集中在同一类别中。 由此,进行类型加宽

我猜它是因为运算符XOR是为布尔和整数定义的。

并且从整数结果到字节的结果转换是信息丢失转换; 因此需要一个明确的演员(来自程序员的点头)。

这更多地与CLI规范中隐式和显式转换的规则有关。 整数(int = System.Int32 = 4字节)比一个字节宽(显然是1个字节!)。 因此,从int到byte的任何转换都可能是一个缩小的转换。 因此,编译器希望您明确这一点。

我以为我记得一个关于这一点的流行问题。

byte + byte = int …为什么?

这似乎是因为在C#语言规范中,它被定义为整数和长整数http://msdn.microsoft.com/en-us/library/aa691307%28v=VS.71%29.aspx

所以,实际发生的是编译器隐式地将字节操作数转换为int,因为这样就不会丢失数据。 但是结果(即int)不能在不丢失数据的情况下进行降级(隐式)。 所以,你需要明确告诉编译器你知道你在做什么!

至于为什么必须将两个字节转换为整数来进行异或?

如果您想深入了解它,CLI规范(分区I)的12.1.2描述了这样一个事实:在评估堆栈上,只能存在int或long。 在评估期间,必须扩展所有较短的整体类型。

不幸的是,我找不到直接与CLI规范的合适链接 – 我有一个本地副本作为PDF,但不记得我从哪里得到它。

FWIW字节a = 25; 字节b = 60; a = a ^ b; 不起作用。 但字节a = 25; 字节b = 60; a ^ = b; 确实有效。