2009-11-09 8 views
10

Parece que ExpressionTrees compilador debe estar cerca de la C# spec en muchos comportamientos, pero a diferencia de C# no hay soporte para la conversión de decimal a cualquier enum-type:¿Este es un error ExpressionTrees? # 2

using System; 
using System.Linq.Expressions; 

class Program 
{ 
    static void Main() 
    { 
    Func<decimal, ConsoleColor> converter1 = x => (ConsoleColor) x; 
    ConsoleColor c1 = converter1(7m); // fine 

    Expression<Func<decimal, ConsoleColor>> expr = x => (ConsoleColor) x; 

    // System.InvalidOperationException was unhandled 
    // No coercion operator is defined between types 
    // 'System.Decimal' and 'System.ConsoleColor'. 

    Func<decimal, ConsoleColor> converter2 = expr.Compile(); 

    ConsoleColor c2 = converter2(7m); 
    } 
} 

Otros rara vez se utiliza C# conversiones explícitas, como double -> enum-type existe y funciona como se explica en la especificación C#, pero no en decimal -> enum-type. ¿Es esto un error?

Respuesta

16

Probablemente sea un error, y probablemente sea mi culpa. Lo siento por eso.

Conseguir conversiones decimales correctas fue una de las partes más difíciles de construir el código de árbol de expresiones correcto en el compilador y el tiempo de ejecución porque las conversiones decimales se implementan realmente como conversiones definidas por el usuario en el tiempo de ejecución, pero se tratan como conversiones integradas por el compilador. El decimal es el único tipo con esta propiedad, y por lo tanto, hay todo tipo de equipos de propósito especial en el analizador para estos casos. De hecho, hay un método llamado IsEnumToDecimalConversion en el analizador para manejar el caso especial de enum nulo en conversión decilla nulable; un caso especial bastante complejo.

Las probabilidades son buenas de que no haya considerado que algún caso vaya en sentido contrario, y como resultado se generó un código incorrecto. Gracias por la nota; Enviaré esto al equipo de prueba, y veremos si podemos obtener una reproducción. Las probabilidades son buenas, si esto resulta ser un error de buena fe, esto no se solucionará para la versión inicial C# 4; en este punto, solo tomamos insectos "el usuario es electrocutado por el compilador" para que la liberación sea estable.

+0

No sabía que los humanos sufrieron daños al crear el lenguaje C# :) –

+0

"las conversiones decimales en realidad se implementan como conversiones definidas por el usuario en el tiempo de ejecución, pero el compilador las trata como conversiones incorporadas": ¿Qué significa esto? , y por qué se hizo de esta manera? – Brian

+2

@Brian: cuando realiza una conversión de cambio de representación, digamos int a double, hay una instrucción IL que realiza exactamente esa conversión. Cuando haces decimal a double, en realidad generamos código para llamar a un método para hacer la conversión; no hay instrucciones de conversión integradas a CLR para decimales. Pero desde la perspectiva de * language * queremos que las conversiones decimales para * aparecer * sean conversiones incorporadas al lenguaje; tenemos diferentes reglas para las conversiones incorporadas y proporcionadas por el usuario. Así que tenemos que construir un escenario especial para ocultar lo que está sucediendo detrás de las escenas con decimales. –

3

No es una respuesta real, sin embargo, que estoy investigando, pero la primera línea se compila como:

Func<decimal, ConsoleColor> converter1 = x => (ConsoleColor)(int)x; 

Si intenta crear una expresión de la lambda anterior, va a trabajar.

EDIT: En la especificación de C#, §6.2.2, se puede leer:

Una conversión enumeración explícita entre dos tipos son procesados ​​por el tratamiento de cualquier participante de enumeración de tipo como el tipo subyacente de ese tipo de enumeración, y luego realizar una conversión implícita o explícita entre los tipos resultantes. Por ejemplo, dado un tipo de enumeración E con un tipo subyacente de int, una conversión de de E a byte se procesa como una conversión numérica explícita (§6.2.1) de int a byte, y una conversión de byte a E se procesa como una conversión numérica implícita (§6.1.2) de byte a int.

Así que los moldes explícitos de enum a decimal se manejan específicamente, es por eso que obtienes los moldes anidados (int y luego en decimal). Pero no puedo ver por qué el compilador no analiza el cuerpo lambda de la misma manera en ambos casos.

+2

El compilador probablemente emita la conversión anidada en otra pasada. En este caso, solo crea un nodo Convert que falla en el tiempo de ejecución. Si se trata de un error del compilador que debe emitir la conversión anidada o un error de la API de Expresión que debería comprender la conversión de decimal a enum, se deja al lector. Por mi parte, creo que es responsabilidad de csc emitir el nodo de conversión adecuado. –

+0

Estoy de acuerdo.De hecho, obtienes el error de compilación en la línea "expr = lambda". Por lo tanto, el compilador no intenta emitir el nodo Convertir adicional o cualquier otra cosa; considera que el cuerpo lambda no es válido, lo cual no es, de acuerdo con la especificación C#. –

+0

Para la conversión 'double -> enum-type' csc no emite Convert' double -> int', simplemente directamente 'double -> enum-type 'y el compilador ExpressionTrees entiende esto bien ... – ControlFlow

Cuestiones relacionadas