c# - sintaxis - ¿Por qué puedo comparar sbyte con todos los otros tipos numéricos*excepto*ulong?
ulong en c# (2)
Puede hacer comparaciones>, <, ==, etc. entre sbyte y byte, int, uint, short, ushort, long, double y float. Pero no ulong.
Mi cerebro está explotando. ¿Alguien puede explicar por qué sbyte puede compararse con uint pero no con ulong?
public bool sbyte_ulong_compare(sbyte x, ulong y)
{
return x < y; // compiler error CS0019
}
Además, el uso unchecked
marcar no hace que las cosas funcionen mejor. El cerebro se derrite.
Otra edición. Esto funciona:
public bool sbyte_ulong_compare(sbyte x, ulong y)
{
//
// returns x < y
//
if (x < 0)
return true;
if (y > 127)
return true;
return ((long)x < (long)y);
}
Cuando compara dos enteros de diferentes tipos de enteros, el tipo de la operación es el tipo de entero más pequeño que puede representar el rango completo de ambos operandos combinados. Si compara un byte con signo con una uint, el tipo de operación es largo, ya que tiene un rango suficiente para cubrir la parte negativa del byte con signo y la parte positiva de la uint.
Cuando intenta comparar sbyte y ulong, no hay un tipo entero que pueda abarcar los rangos de ulong y la parte negativa del byte con signo. El compilador solo considera tipos de enteros incorporados. La promoción implícita a Decimal no se incluye porque Decimal no es un tipo entero y, por razones de rendimiento.
En el ejemplo del segundo código, ya que ha precalificado los operandos, puede encasillar los operandos de forma segura a un tipo entero común que no abarca el rango de ambos operandos. Tenga en cuenta también que en su segundo ejemplo, podría realizar la conversión a byte (en lugar de largo) sin pérdida de información, ya que ya ha establecido que el valor de ulong es menor que 127 y el valor de sbyte no es negativo.
El compilador de C # no "ve" que ha precalificado los operandos y que, lógicamente, los valores de los operandos están dentro del rango de bytes, y el compilador no genera código para realizar dichas precalificaciones.
Algunos idiomas emiten códigos de precalificación similares a los del segundo ejemplo para admitir comparaciones entre tipos que no tienen un tipo de superconjunto común. Tomas un golpe de rendimiento y memoria (tamaño de código) para esto. C # probablemente no emita este tipo de código de precalificación en el espíritu de no querer "recompensar" las malas prácticas de codificación. Si está comparando un valor firmado y un valor ulongado, debe conocer y responsabilizarse de los costos.
En teoría del lenguaje hay una rama de tipo inferencia llamada (creo) tipo álgebra que realiza un seguimiento de las pruebas contra las variables y reduce de manera dinámica el rango del tipo de la variable a medida que se descubren nuevas restricciones en el flujo del código. Esta forma de inferencia de tipo le permitiría comparar los operandos sin encasillar en su segundo ejemplo porque vería que ha precalificado los operandos en el rango de bytes. C # no hace este tipo de inferencias tipo. Creo que Haskell o F # podrían.
Las respuestas de dthorpe y Jon son cercanas pero no del todo correctas.
El razonamiento correcto es el siguiente.
La especificación establece:
Para una operación de la forma x op y, donde op es un operador de comparación, la resolución de sobrecarga se aplica para seleccionar una implementación de operador específica.
OK, ¿con qué implementaciones de operadores tiene que trabajar la resolución de sobrecarga? Son:
bool operator <(int x, int y);
bool operator <(uint x, uint y);
bool operator <(long x, long y);
bool operator <(ulong x, ulong y);
bool operator <(float x, float y);
bool operator <(double x, double y);
bool operator <(decimal x, decimal y);
Más el operador enumeración menor que para todos los tipos enumerados, más las versiones elevadas a nulos de cada uno de los anteriores.
La resolución de sobrecarga primero debe eliminar a los operadores no aplicables , y luego del conjunto restante de operadores aplicables, determinar el mejor operador.
Los operadores int, uint, long y enum (y sus formas elevadas) se eliminan porque ulong no se convierte implícitamente a esos tipos.
Los operadores uint y ulong (y sus formas elevadas) se eliminan porque sbyte no se convierte implícitamente a esos tipos.
Eso deja
bool operator <(float x, float y);
bool operator <(double x, double y);
bool operator <(decimal x, decimal y);
y sus formas levantadas. Ahora debemos determinar el mejor operador de esos seis.
¿Qué queremos decir con "mejor"? Cuando se comparan dos operadores, el que tiene los tipos de operandos más específicos es el mejor. Por "más específico" quiero decir que "Tigre" es más específico que "Animal" porque todos los Tigres son convertibles en Animales, pero no todos los Animales son convertibles en Tigres.
Claramente, las formas sin relieve son mejores que todas las formas elevadas correspondientes. Un tipo no anulable es más específico que su tipo anulable correspondiente porque un tipo no anulable siempre es convertible a su tipo anulable, pero no viceversa. Podemos eliminar las formas levantadas.
Eso deja tres. ¿Cuál de los tres es el mejor?
El flotador es más específico que el doble. Cada flotador se puede convertir al doble, pero no todo doble se puede convertir a flotar. Por lo tanto se elimina el doble. Eso deja dos.
bool operator <(float x, float y);
bool operator <(decimal x, decimal y);
¿Cuál de estos es el mejor? No hay conversión implícita de float a decimal. No hay conversión implícita de decimal a flotante. Por lo tanto, ninguno es mejor que el otro.
Por lo tanto, ningún mejor operador puede ser determinado. La resolución de sobrecarga falla.
Hemos decidido informar un mensaje de error genérico que simplemente dice que no hay tal operador que haga lo que usted quiere, en lugar de dar el mensaje de error aparentemente extraño y confuso "la resolución de sobrecarga del operador falló porque la flotación no es mejor ni peor que la decimal". Creo que es una opción de diseño razonable.