看看这段c#代码:
byte x = 1;
byte y = 2;
byte z = x + y; // ERROR: Cannot implicitly convert type 'int' to 'byte'
在字节(或短)类型上执行的任何数学运算的结果都隐式地转换回整数。解决方案是显式地将结果转换回一个字节:
byte z = (byte)(x + y); // this works
我想知道的是为什么?是建筑吗?哲学吗?
我们有:
Int + Int = Int
长+长=长
浮+浮=浮
Double + Double = Double
所以为什么不呢:
字节+字节=字节
空头+空头=空头?
一点背景知识:我正在对“小数字”(即< 8)执行一个长列表的计算,并将中间结果存储在一个大数组中。使用字节数组(而不是int数组)更快(因为缓存命中)。但是大量的字节强制转换散布在代码中,使得代码更加难以阅读。
我已经测试了字节和int之间的性能。
int值:
class Program
{
private int a,b,c,d,e,f;
public Program()
{
a = 1;
b = 2;
c = (a + b);
d = (a - b);
e = (b / a);
f = (c * b);
}
static void Main(string[] args)
{
int max = 10000000;
DateTime start = DateTime.Now;
Program[] tab = new Program[max];
for (int i = 0; i < max; i++)
{
tab[i] = new Program();
}
DateTime stop = DateTime.Now;
Debug.WriteLine(stop.Subtract(start).TotalSeconds);
}
}
使用字节值:
class Program
{
private byte a,b,c,d,e,f;
public Program()
{
a = 1;
b = 2;
c = (byte)(a + b);
d = (byte)(a - b);
e = (byte)(b / a);
f = (byte)(c * b);
}
static void Main(string[] args)
{
int max = 10000000;
DateTime start = DateTime.Now;
Program[] tab = new Program[max];
for (int i = 0; i < max; i++)
{
tab[i] = new Program();
}
DateTime stop = DateTime.Now;
Debug.WriteLine(stop.Subtract(start).TotalSeconds);
}
}
结果如下:
字节:3.57s 157mo, 3.71s 171mo, 3.74s 168mo, CPU ~= 30%
int: 4.05s 298mo, 3.92s 278mo, 4.28 294mo with CPU ~= 27%
结论:
字节使用更多的CPU,但它消耗更少的内存,它更快(可能是因为有更少的字节分配)
我想我以前在什么地方见过这个。从这篇文章,旧的新事物:
假设我们生活在一个幻想的世界
对'byte'的操作导致了什么
“字节”。
byte b = 32;
byte c = 240;
int i = b + c; // what is i?
在这个幻想世界里,i的值
应该是16岁!为什么?因为这两个
+操作符的操作数都是
字节,因此“b+c”的和计算为
一个字节,由于
整数溢出。(而且,正如我所指出的
之前,整数溢出是新的
安全攻击向量。)
编辑:Raymond实质上是在捍卫C和c++最初采用的方法。在评论中,他从语言向后兼容的角度为c#采用了相同的方法进行了辩护。
从.NET框架代码:
// bytes
private static object AddByte(byte Left, byte Right)
{
short num = (short) (Left + Right);
if (num > 0xff)
{
return num;
}
return (byte) num;
}
// shorts (int16)
private static object AddInt16(short Left, short Right)
{
int num = Left + Right;
if ((num <= 0x7fff) && (num >= -32768))
{
return (short) num;
}
return num;
}
使用。net 3.5及以上版本进行简化:
public static class Extensions
{
public static byte Add(this byte a, byte b)
{
return (byte)(a + b);
}
}
现在你可以做:
byte a = 1, b = 2, c;
c = a.Add(b);
C#
ECMA-334指出,只有在int+int、uint+uint、long+long和ulong+ulong (ECMA-334 14.7.4)上,加法才被定义为合法。因此,这些是关于14.4.2需要考虑的候选操作。由于存在从byte到int、uint、long和ulong的隐式强制转换,所有加法函数成员都是14.4.2.1下适用的函数成员。我们必须根据14.4.2.3中的规则找到最佳隐式转换:
将(C1)转换为int(T1)比将(C2)转换为uint(T2)或ulong(T2)更好,因为:
如果T1是int, T2是uint或ulong, C1是更好的转换。
将(C1)转换为int(T1)比将(C2)转换为long(T2)更好,因为从int转换为long存在隐式转换:
如果存在从T1到T2的隐式转换,且不存在从T2到T1的隐式转换,则C1是更好的转换。
因此使用int+int函数,该函数返回int型。
可以说它深埋在c#规范中。
CLI
CLI只对6种类型(int32、native int、int64、F、O和&)起作用。(ECMA-335分区3节1.5)
Byte (int8)不是这些类型之一,在添加之前会自动强制转换为int32。(ECMA-335分区3节1.6)
这是我对这个话题的大部分回答,首先是针对这里的一个类似问题。
默认情况下,所有小于Int32的整数运算在计算前四舍五入到32位。结果为Int32的原因仅仅是让它在计算后保持原样。如果检查MSIL算术操作码,它们操作的唯一整型数字类型是Int32和Int64。这是“故意的”。
如果您希望结果以Int16格式返回,则在代码中执行强制转换或编译器(假设)在“底层”发出转换都无关紧要。
例如,要执行Int16算术:
short a = 2, b = 3;
short c = (short) (a + b);
这两个数字将扩展为32位,然后相加,然后截短为16位,这是MS所希望的。
使用短(或字节)的优势主要是在有大量数据(图形数据、流等)的情况下存储。