¿Por qué byte + byte = int?
Mirando este código C#:
byte x = 1;
byte y = 2;
byte z = x + y; // ERROR: Cannot implicitly convert type 'int' to 'byte'
El resultado de cualquier cálculo realizado en byte
(o short
) tipos se convierte implícitamente en un número entero. La solución es convertir explícitamente el resultado a un byte:
byte z = (byte)(x + y); // this works
Lo que me pregunto es ¿por qué? ¿Es arquitectónico? ¿Filosófico?
Tenemos:
int
+int
=int
long
+long
=long
float
+float
=float
double
+double
=double
Entonces por qué no:
byte
+byte
=byte
short
+short
=short
?
Un poco de historia: estoy realizando una larga lista de cálculos con "números pequeños" (es decir, <8) y almacenando los resultados intermedios en una matriz grande. Usar una matriz de bytes (en lugar de una matriz int) es más rápido (debido a los aciertos de caché). Pero la extensa difusión de bytes repartida por el código lo hace mucho más ilegible.
La tercera línea de tu fragmento de código:
byte z = x + y;
en realidad significa
byte z = (int) x + (int) y;
Por lo tanto, no existe una operación + en bytes, los bytes se convierten primero en números enteros y el resultado de la suma de dos números enteros es un número entero (32 bits).
En términos de "por qué sucede", es porque no hay operadores definidos por C# para aritmética con byte, sbyte, short o ushort, tal como otros han dicho. Esta respuesta trata sobre por qué esos operadores no están definidos.
Creo que es básicamente por el rendimiento. Los procesadores tienen operaciones nativas para hacer aritmética con 32 bits muy rápidamente. Se podría realizar la conversión del resultado a un byte automáticamente , pero daría lugar a penalizaciones de rendimiento en el caso de que en realidad no desee ese comportamiento.
Creo que esto se menciona en uno de los estándares de C# comentados. Mirando...
EDITAR: Es molesto que ahora haya revisado la especificación ECMA C# 2 anotada, la especificación MS C# 3 anotada y la especificación CLI de anotación, y ninguna de ellas menciona esto hasta donde puedo ver. Estoy seguro de haber visto el motivo indicado anteriormente, pero estoy desconcertado si sé dónde. Disculpas, fans de referencia :(
Del artículo ¿Por qué las operaciones con "byte" dan como resultado "int"? en el blog de Raymond Chen, The Old New Thing:
Supongamos que viviéramos en un mundo de fantasía donde las operaciones con "byte" daban como resultado "byte".
byte b = 32; byte c = 240; int i = b + c; // what is i?
¡En este mundo de fantasía, el valor de i sería 16! ¿Por qué? Debido a que los dos operandos del operador + son ambos bytes, la suma "b+c" se calcula como un byte, lo que da como resultado 16 debido al desbordamiento de enteros. (Y, como señalé anteriormente, el desbordamiento de enteros es el nuevo vector de ataque a la seguridad ).
Raymond defiende, esencialmente, el enfoque que adoptaron originalmente C y C++. En los comentarios, defiende el hecho de que C# adopta el mismo enfoque, basándose en la compatibilidad del lenguaje con versiones anteriores.
C#
ECMA-334 establece que la suma solo se define como legal en int+int, uint+uint, long+long y ulong+ulong (ECMA-334 14.7.4). Como tales, estas son las operaciones candidatas a considerar con respecto a 14.4.2. Debido a que hay conversiones implícitas de byte a int, uint, long y ulong, todos los miembros de la función de suma son miembros de función aplicables según 14.4.2.1. Tenemos que encontrar la mejor conversión implícita según las reglas de 14.4.2.3:
Convertir(C1) a int(T1) es mejor que convertir(C2) a uint(T2) o ulong(T2) porque:
- Si T1 es int y T2 es uint o ulong, C1 es la mejor conversión.
Convertir(C1) a int(T1) es mejor que convertir(C2) a long(T2) porque hay una conversión implícita de int a long:
- Si existe una conversión implícita de T1 a T2 y no existe ninguna conversión implícita de T2 a T1, C1 es la mejor conversión.
Por lo tanto, se utiliza la función int+int, que devuelve un int.
Lo cual es un largo camino para decir que está enterrado muy profundamente en la especificación de C#.
CLI
La CLI opera solo en 6 tipos (int32, native int, int64, F, O y &). (ECMA-335 partición 3 sección 1.5)
El byte (int8) no es uno de esos tipos y se convierte automáticamente en int32 antes de la adición. (ECMA-335 partición 3 sección 1.6)