C # Переключение выражений, возвращающих другой результат

13

Я перешел на C # 8 в одном из моих проектов. И я переместил все мои switchзаявления в выражения. Однако я узнал, что мой проект начал работать по-другому, и я обнаружил, что это было из-за switchвыражения. Давайте возьмем этот код, например

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(),
        };
    }
}

Я написал результат в виде комментария, но при использовании классического переключателя приведение значения возвращает значение в ожидаемом типе, но при использовании выражения переключателя возвращается тип «Double», в результате чего byte[]при получении байты значения.

В чем разница между двумя? Что мне не хватает?

Expressingx
источник
1
Я не могу точно объяснить, почему и как это происходит, но если вы посмотрите на декомпилированную версию своего кода здесь ( gist.github.com/MaDOS/4904683d461d022e4b24f4080009ae5e ), вы заметите, что компилятор, кажется, замечает, что все возможные возвращаемые типы выражения будет помещаться в двойное число и автоматически объявляет двойное, где оно будет хранить любой результат, который будет возвращен. ( gist.github.com/MaDOS/… )
Робин Б

Ответы:

17

В вашей форме оператора switch каждая рука возвращает значение напрямую. Он напрямую конвертируется из числового типа objectв тип возврата метода.

Ваша форма выражения переключателя немного отличается. Сначала он извлекает результат из выражения switch, затем преобразует этот результат в объявленный тип возвращаемого значения. Так какой тип выражения switch? Это «лучший» тип из всех типов отдельных выражений в руках выражения switch.

Все эти типы могут быть неявно преобразованы в double(который является одним из типов), так что это лучший тип. Таким образом, ваш метод выражения switch эквивалентен:

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;
}

Вы можете увидеть этот «лучший тип», не используя выражение переключателя, используя неявно типизированные массивы:

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

Здесь тип arrayвыводится double[].

Джон Скит
источник