C # Alihkan ekspresi yang mengembalikan hasil berbeda

13

Saya telah beralih ke C # 8 pada salah satu proyek saya. Dan saya telah memindahkan semua switchpernyataan saya ke ekspresi. Namun saya menemukan bahwa proyek saya mulai bekerja secara berbeda dan saya menemukan bahwa itu karena switchekspresi. Mari kita dapatkan kode ini misalnya

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

Saya telah menulis hasilnya sebagai komentar, tetapi tl; dr ketika switch klasik digunakan casting nilai mengembalikan nilai dalam Type yang diharapkan, tetapi ketika switch switch digunakan mengembalikan tipe "Double", menghasilkan perbedaan byte[] ketika mendapatkan byte nilai.

Apa perbedaan keduanya? Apa yang saya lewatkan?

Expressingx
sumber
1
Saya tidak dapat menjelaskan dengan tepat mengapa dan bagaimana hal ini terjadi tetapi jika Anda melihat versi kode Anda yang telah didekompilasi di sini ( gist.github.com/MaDOS/4904683d461d022e4b24f4080009ae5e ) Anda melihat bahwa kompiler tampaknya memperhatikan bahwa semua jenis yang mungkin kembali muncul. dari ekspresi akan masuk ke dalam double dan secara otomatis menyatakan double di mana ia akan menyimpan hasil apa pun yang akan dikembalikan. ( gist.github.com/MaDOS/… )
Robin B

Jawaban:

17

Di saklar Anda formulir pernyataan , setiap lengan mengembalikan nilai secara langsung. Itu mengkonversi dari tipe numerik langsung ke object, karena itu secara efektif jenis pengembalian metode.

Saklar Anda Bentuk ekspresi sedikit berbeda. Pertama-tama mengekstrak hasil dari ekspresi sakelar, lalu mengonversi hasil itu ke tipe pengembalian yang dinyatakan. Jadi apa jenis ekspresi saklar? Ini adalah tipe "terbaik" dari semua tipe ekspresi individu di lengan ekspresi switch.

Semua tipe tersebut dapat secara implisit dikonversi double (yang merupakan salah satu tipe itu sendiri), jadi itulah tipe terbaik. Jadi metode pengalihan ekspresi Anda sama dengan:

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

Anda dapat melihat "tipe terbaik" ini tanpa menggunakan ekspresi sakelar, menggunakan larik yang diketik secara implisit:

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

Di sini jenis arraydisimpulkan double[].

Jon Skeet
sumber