C # Cambiar expresiones que devuelven resultados diferentes

13

Me cambié a C # 8 en uno de mis proyectos. Y he estado moviendo todas mis switchdeclaraciones a expresiones. Sin embargo, descubrí que mi proyecto comenzó a funcionar de manera diferente y descubrí que fue por la switchexpresión. Vamos a obtener este código, por ejemplo

class Program
{
    public enum DataType
    {
        Single,
        Double,
        UInt16,
        UInt32,
        UInt64,
        Int16,
        Int32,
        Int64,
        Byte
    }

    static void Main(string[] args)
    {
        dynamic value1 = 5;
        dynamic value2 = 6;

        var casted = CastToType(value1, DataType.Int16);
        var casted1 = CastToTypeExpression(value2, DataType.Int16);


        var type = casted.GetType(); // Int16
        var type1 = casted1.GetType(); // Double
        var bytes = BitConverter.GetBytes(casted); // byte arr with 2 el => [5, 0] <- expected behavior 
        var bytes1 = BitConverter.GetBytes(casted1); // byte arr with 8 el => [0, 0, 0, 0, 0, 0, 24, 64]
    }

    public static dynamic CastToType(dynamic value, DataType type)
    {
        switch (type)
        {
            case DataType.Byte:
                return (byte)value;
            case DataType.Double:
                return (double)value;
            case DataType.Int16:
                return (short)value;
            case DataType.Int32:
                return (int)value;
            case DataType.Int64:
                return (long)value;
            case DataType.Single:
                return (float)value;
            case DataType.UInt16:
                return (ushort)value;
            case DataType.UInt32:
                return (uint)value;
            case DataType.UInt64:
                return (ulong)value;
            default: throw new InvalidCastException();
        }
    }

    public static dynamic CastToTypeExpression(dynamic value, DataType type)
    {
        return type switch
        {
            DataType.Byte => (byte)value,
            DataType.Double => (double)value,
            DataType.Int16 => (short)value,
            DataType.Int32 => (int)value,
            DataType.Int64 => (long)value,
            DataType.Single => (float)value,
            DataType.UInt16 => (ushort)value,
            DataType.UInt32 => (uint)value,
            DataType.UInt64 => (ulong)value,
            _ => throw new InvalidCastException(),
        };
    }
}

Escribí el resultado como un comentario, pero tl; dr cuando se usa el interruptor clásico, el valor devuelve el valor en el Tipo esperado, pero cuando se usa la expresión del interruptor, devuelve el tipo de "Doble", que resulta diferente byte[]al obtener el valor bytes del valor.

¿Cuál es la diferencia entre los dos? ¿Qué extraño?

Expressingx
fuente
1
No puedo explicar exactamente por qué y cómo sucede esto, pero si echa un vistazo a una versión descompilada de su código aquí ( gist.github.com/MaDOS/4904683d461d022e4b24f4080009ae5e ), observa que el compilador parece darse cuenta de que todos los tipos posibles regresan de la expresión se ajustará a un doble y automáticamente declarará un doble donde almacenará cualquier resultado que se devuelva. ( gist.github.com/MaDOS/… )
Robin B

Respuestas:

17

En su formulario de declaración de cambio , cada brazo está devolviendo un valor directamente. Se está convirtiendo del tipo numérico directamente a object, ya que ese es efectivamente el tipo de retorno del método.

Su forma de expresión de cambio es ligeramente diferente. Primero extrae un resultado de la expresión de cambio, luego convierte ese resultado al tipo de retorno declarado. Entonces, ¿cuál es el tipo de expresión de cambio? Es el tipo "mejor" de todos los tipos de expresiones individuales en los brazos de la expresión de cambio.

Todos esos tipos se pueden convertir implícitamente a double(que es uno de los tipos en sí), por lo que es el mejor tipo. Entonces, su método de expresión de interruptor es equivalente a:

public static dynamic CastToTypeExpression(dynamic value, DataType type)
{
    double result = type switch
    {
        DataType.Byte => (byte)value,
        DataType.Double => (double)value,
        DataType.Int16 => (short)value,
        DataType.Int32 => (int)value,
        DataType.Int64 => (long)value,
        DataType.Single => (float)value,
        DataType.UInt16 => (ushort)value,
        DataType.UInt32 => (uint)value,
        DataType.UInt64 => (ulong)value,
        _ => throw new InvalidCastException(),
    };
    return result;
}

Puede ver este "mejor tipo" sin utilizar una expresión de cambio, utilizando matrices tipadas implícitamente:

var array = new[]
{
    (byte) 0, 0.0, (short) 0, 0,
    0L, 0f, (ushort) 0, 0U, 0UL
};

Aquí arrayse infiere que el tipo de ser double[].

Jon Skeet
fuente