Expressions C # Switch renvoyant un résultat différent

13

Je suis passé en C # 8 sur l'un de mes projets. Et j'ai déplacé toutes mes switchdéclarations vers des expressions. Cependant, j'ai découvert que mon projet a commencé à fonctionner différemment et j'ai découvert que c'était à cause de l' switchexpression. Permet d'obtenir ce code par exemple

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

J'ai écrit le résultat sous forme de commentaire, mais tl; dr lorsque le commutateur classique est utilisé, la conversion de la valeur renvoie la valeur dans le type attendu, mais lorsque l'expression de commutateur est utilisée, elle renvoie le type "Double", résultant en différents byte[]lors de l'obtention du octets de la valeur.

Quelle est la différence entre les deux? Qu'est-ce que je manque?

Expressingx
la source
1
Je ne peux pas expliquer exactement pourquoi et comment cela se produit, mais si vous regardez une version décompilée de votre code ici ( gist.github.com/MaDOS/4904683d461d022e4b24f4080009ae5e ), vous remarquez que le compilateur semble remarquer que tous les types de retour possibles de l'expression rentre dans un double et déclare automatiquement un double où il stockera tout résultat qui sera retourné. ( gist.github.com/MaDOS/… )
Robin B

Réponses:

17

Dans votre formulaire de déclaration de commutateur , chaque branche renvoie directement une valeur. C'est la conversion directe du type numérique en object, car c'est effectivement le type de retour de la méthode.

Votre forme d' expression de commutateur est légèrement différente. Il extrait d'abord un résultat de l'expression de commutateur, puis convertit ce résultat en type de retour déclaré. Alors, quel est le type de l'expression switch? C'est le "meilleur" type parmi tous les types d'expressions individuelles dans les bras de l'expression switch.

Tous ces types peuvent être implicitement convertis en double(qui est l'un des types lui-même), c'est donc le meilleur type. Votre méthode d'expression de commutateur est donc équivalente à:

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

Vous pouvez voir ce "meilleur type" sans utiliser d'expression de commutateur, en utilisant des tableaux typés implicitement:

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

Ici, le type de arrayest supposé être double[].

Jon Skeet
la source