C # Switch-Ausdrücke, die unterschiedliche Ergebnisse zurückgeben

13

Ich habe bei einem meiner Projekte zu C # 8 gewechselt. Und ich habe alle meine switchAussagen auf Ausdrücke verschoben. Ich fand jedoch heraus, dass mein Projekt anders funktionierte und ich fand heraus, dass es am switchAusdruck lag. Holen wir uns diesen Code zum Beispiel

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

Ich habe das Ergebnis als Kommentar geschrieben, aber tl; dr, wenn der klassische Schalter verwendet wird, gibt der Wert den Wert im erwarteten Typ zurück, aber wenn der Schalterausdruck verwendet wird, gibt er den Typ "Double" zurück, was zu einem anderen Wert führt, wenn der Wert byte[]abgerufen wird Bytes des Wertes.

Was ist der Unterschied zwischen den beiden? Was vermisse ich?

Expressingx
quelle
1
Ich kann nicht genau erklären, warum und wie dies geschieht, aber wenn Sie sich hier eine dekompilierte Version Ihres Codes ansehen ( gist.github.com/MaDOS/4904683d461d022e4b24f4080009ae5e ), bemerken Sie, dass der Compiler zu bemerken scheint, dass alle möglichen Typen zurückgegeben werden des Ausdrucks passt in ein Double und deklariert automatisch ein Double, in dem das zurückgegebene Ergebnis gespeichert wird. ( gist.github.com/MaDOS/… )
Robin B

Antworten:

17

In Ihrem switch- Anweisungsformular gibt jeder Arm direkt einen Wert zurück. Es wird direkt vom numerischen Typ in konvertiert object, da dies effektiv der Rückgabetyp der Methode ist.

Ihre Switch Ausdruck Form ist etwas anders. Es extrahiert zuerst ein Ergebnis aus dem switch-Ausdruck und konvertiert dieses Ergebnis dann in den deklarierten Rückgabetyp. Was ist die Art des Schalterausdrucks? Es ist der "beste" Typ aus allen Typen der einzelnen Ausdrücke in den Armen des Schalterausdrucks.

Alle diese Typen können implizit in konvertiert werden double(dies ist einer der Typen selbst). Das ist also der beste Typ. Ihre Switch-Ausdrucksmethode entspricht also:

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

Sie können diesen "besten Typ" ohne Verwendung eines Schalterausdrucks mit implizit typisierten Arrays sehen:

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

Hier ist die Art der zu arrayentnehmen ist , zu sein double[].

Jon Skeet
quelle