Expression trees - unnecessary conversion to int32

后端 未结 2 1105
一个人的身影
一个人的身影 2021-02-19 10:41

Expression trees seem to build an unnecessary conversion when working with bytes and shorts, they convert both sides (in binary expressions for instance) to int32.

This

相关标签:
2条回答
  • 2021-02-19 10:49

    That's really interesting; unfortunately, the rules of the expression-tree compiler are not formally specified - there is a brief "are elsewhere" in the specification, but : they aren't really.

    If it is causing a problem, you could try to spot and remove it - something like below, which is 100% untested and use-at-own-risk, etc:

    static void Main()
    {
        Console.WriteLine(((Expression<Func<short, short, bool>>)((s, s1) => s == s1)).Unmunge());
        Console.WriteLine(((Expression<Func<byte, byte, bool>>)((s, s1) => s == s1)).Unmunge());  
    }
    static Expression<T> Unmunge<T>(this Expression<T> expression)
    {
        return (Expression<T>)RedundantConversionVisitor.Default.Visit(expression);
    }
    class RedundantConversionVisitor : ExpressionVisitor
    {
        private RedundantConversionVisitor() { }
        public static readonly RedundantConversionVisitor Default = new RedundantConversionVisitor();
        protected override Expression VisitBinary(BinaryExpression node)
        {
            if(node.Type == typeof(bool) && node.Method == null
                && node.Left.NodeType == ExpressionType.Convert && node.Right.NodeType == ExpressionType.Convert
                && node.Left.Type == node.Right.Type)
            {
                UnaryExpression lhs = (UnaryExpression)node.Left, rhs = (UnaryExpression)node.Right;
                if (lhs.Method == null && rhs.Method == null && lhs.Operand.Type == rhs.Operand.Type)
                {
                    // work directly on the inner values
                    return Expression.MakeBinary(node.NodeType, lhs.Operand, rhs.Operand, node.IsLiftedToNull, node.Method);
                }
            }
            return base.VisitBinary(node);
        }
    }
    

    Output before:

    (s, s1) => (Convert(s) == Convert(s1))
    (s, s1) => (Convert(s) == Convert(s1))
    

    Output after:

    (s, s1) => (s == s1)
    (s, s1) => (s == s1)
    
    0 讨论(0)
  • 2021-02-19 11:08

    To answer your question:

    Why Expression trees seem to build an unnecessary conversion when working with bytes and shorts... So the question is, what is the reason for this behavior?

    The answer is hidden in the fact, that C# types short, ushort, byte and sbyte lack the arithmetic, comparison... operators:

    Extract: 4.1.5 Integral types

    For the binary +, –, *, /, %, &, ^, |, ==, !=, >, <, >=, and <= operators, the operands are converted to type T, where T is the first of int, uint, long, and ulong that can fully represent all possible values of both operands. The operation is then performed using the precision of type T, and the type of the result is T (or bool for the relational operators). It is not permitted for one operand to be of type long and the other to be of type ulong with the binary operators.

    The 7.9.1 Integer comparison operators describes available operators and their operands

    bool operator ==(int x, int y);
    bool operator ==(uint x, uint y);
    bool operator ==(long x, long y);
    bool operator ==(ulong x, ulong y);
    ... // other operators, only for int, uint, long, ulong
    

    The conversion is done for you by Compiler (the reason why you succeed to build that without explicit conversion)

    Because there are no operators working with short... the conversion must be applied. And of course, it later depends on the LINQ provider, how to convert such "expression" into SQL.

    0 讨论(0)
提交回复
热议问题