2012-06-18 15 views
24

Tengo curiosidad por saber por qué el compilador de C# solo me da un mensaje de error para la segunda instrucción if.El operador ">" no se puede aplicar al tipo 'ulong' e 'int'

enum Permissions : ulong 
{ 
    ViewListItems = 1L, 
} 

public void Method() 
{ 
    int mask = 138612833; 
    int compare = 32; 

    if (mask > 0 & (ulong)Permissions.ViewListItems > 32) 
    { 
     //Works 
    } 

    if (mask > 0 & (ulong)Permissions.ViewListItems > compare) 
    { 
     //Operator '>' cannot be applied to operands of type 'ulong' and 'int' 
    } 
} 
+1

El motivo de este error es claro. En el segundo ejemplo, intenta realizar una operación ilegal con un número entero. En el primer ejemplo, el '32' se trata como un' ulong'. Deberías hacer que 'mask' sea' ulong'. –

+0

@Ramhound O bien eso (make 'compare' an' ulong'), o make 'compare' una variable' const', por lo que 'const int compare = 32;'. Las expresiones constantes se convertirán felizmente de 'int' a' ulong' en tiempo de compilación si no son negativas, algo que se conoce con el nombre _Conversiones de expresión constante explícita_. –

Respuesta

30

He estado experimentando con esto, usando ILSpy para examinar la salida, y esto es lo que he descubierto.

Obviamente, en su segundo caso, este es un error: no puede comparar un ulong y un int porque no hay un tipo al que pueda forzar a ambos. Un ulong puede ser demasiado grande para un long, y un int puede ser negativo.

En su primer caso, sin embargo, el compilador está siendo inteligente. Se da cuenta de que const 1> const 32 nunca es verdadero, y no incluye su declaración if en el resultado compilado en absoluto. (Debe dar una advertencia para el código inalcanzable). Es lo mismo si define y usa un const int en lugar de un literal, o incluso si emite el literal explícitamente (es decir, (int)32).

Pero entonces no es el compilador de comparando con éxito un ulong con un int, que acaba dijimos era imposible?

Aparentemente no. Entonces, ¿qué está pasando?

Intente hacer algo en las siguientes líneas. (Toma de entrada y salida de la escritura por lo que el compilador no compilar nada.)

const int thirtytwo = 32; 
static void Main(string[] args) 
{ 
    ulong x = ulong.Parse(Console.ReadLine()); 
    bool gt = x > thirtytwo; 
    Console.WriteLine(gt); 
} 

Esto compilará, a pesar de que la ulong es una variable, y aunque el resultado no se conoce en tiempo de compilación. Echar un vistazo a la salida de ILSpy:

private static void Main(string[] args) 
{ 
    ulong x = ulong.Parse(Console.ReadLine()); 
    bool gt = x > 32uL;  /* Oh look, a ulong. */ 
    Console.WriteLine(gt); 
} 

Por lo tanto, el compilador es, de hecho, el tratamiento de su const int como ulong. Si realiza thirtytwo = -1, el código no se compila, aunque sepamos que gt será siempre verdadero. El compilador no puede comparar un ulong con un int.

También tenga en cuenta que si se hace una xlong en lugar de un ulong, el compilador genera 32L en lugar de 32 como un entero, a pesar de que no se tienen a. (Se puede comparar un int y una long en tiempo de ejecución.)

Esto apunta a que el compilador no tratar 32 como ulong en el primer caso porque tiene a, simplemente porque puede coincidir con el tipo de x . Está evitando que el tiempo de ejecución tenga que forzar la constante, y esto es solo una ventaja cuando la coacción no debería ser posible por derecho.

+1

Esta fue una lectura muy interesante, apoyos para la investigación. – Amicable

+1

@Amicable - apoyos a _you_ para una pregunta interesante que me hizo querer investigarlo :) – Rawling

+0

El compilador de C# está completamente de acuerdo con la especificación del lenguaje C# aquí. Cuando una expresión de tipo 'int' es una constante en tiempo de compilación, existe una [conversión de expresión constante implícita] (http://msdn.microsoft.com/en-us/library/aa691286.aspx) de' int' a 'ulong' _proporciona que el valor esté dentro del rango_ de' ulong', es decir, no negativo, que es algo que el compilador de C# puede ver con una expresión constante. '32' es una expresión constante de tipo' int'. Cuando una expresión de tipo 'int' es _not_ una constante de tiempo de compilación, no existe conversión implícita a' ulong'. Ejemplo 'compare'. –

21

No es el CLR que muestra este mensaje de error sino el compilador.

En el primer ejemplo el compilador trata 32 como ulong (o un tipo que es convertir implícitamente a ulong por ejemplo uint) mientras que en el segundo ejemplo se ha declarado explícitamente el tipo como un int. No hay sobrecarga del operador > que acepta un ulong y un int y, por lo tanto, se obtiene un error de compilación.

+7

La página de literales enteros (http://msdn.microsoft.com/en-us/library/aa664674.aspx) dice "Si el literal no tiene sufijo, tiene el primero de estos tipos en que se puede representar su valor: int, uint, long, ulong ". Además, mi Intellisense (información sobre herramientas) dice que '32' es un' int'. Pero esto parece ser lo que está sucediendo ... – Rawling

+1

@Rawling Muy cierto, puedo ver tres posibles razones (me atrevo a decir que hay muchas más): # 1 quizás debido a las diferentes interpretaciones de "valor se puede representar", es decir, el compilador sabe que 32 no puede ser una int, ya que el código no se compilará y probará el siguiente que tenga éxito. Intellisense, obviamente, no evalúa la expresión completa para calcular el tipo, int es generalmente correcto, así que ¿por qué sacrificar el tiempo de cálculo para un caso límite? # 2 la documentación para literales enteros está desactualizada. O # 3 es un error del compilador. –

+0

@Rawling: no hay ningún error que funcione exactamente como debería. El '32 'literal está siendo tratado como un' largo '. Incluso cita la afirmación donde dice que será tratada como 'larga'. –

3

Las respuestas de rich.okelly y rawling son correctas en cuanto a por qué no puedes compararlas directamente. Puede utilizar el método Convert de la clase ToUInt64 para promocionar el int.

if (mask > 0 & (ulong)Permissions.ViewListItems > Convert.ToUInt64(compare)) 
{ 
} 
Cuestiones relacionadas