表达式树 - 不必要的转换为int32

Mor*_*anB 12 c# linq nhibernate expression-trees

表达式树在处理字节和短路时似乎构建了不必要的转换,它们将双方(例如二进制表达式)转换为int32.

这是我见过的一些Linq提供程序中的一个问题,每个都必须剥离这个冗余层才能获得原始表达式.(NHibernate不会删除此层并在SQL查询中创建可怕的CAST).

// no conversion
Console.WriteLine((Expression<Func<int, int, bool>>) ((s, s1) => s == s1));
// converts to int32
Console.WriteLine((Expression<Func<short, short, bool>>) ((s, s1) => s == s1));
// converts to int32
Console.WriteLine((Expression<Func<byte, byte, bool>>) ((s, s1) => s == s1));
Run Code Online (Sandbox Code Playgroud)

如果您尝试构建一个能够进行精确比较的表达式(没有转换),那么您将获得成功.

所以问题是,这种行为的原因是什么?

EDIT .net 4.0 64bit,同样适用于4.5 64bit

Mar*_*ell 5

这真的很有趣; 遗憾的是,表达式树编译器的规则没有正式指定 - 规范中有一个简短的"在其他地方",但是:它们并不是真的.

如果它导致问题,你可以尝试发现并删除它 - 如下所示,这是100%未经测试和使用自己风险等:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出之前:

(s, s1) => (Convert(s) == Convert(s1))
(s, s1) => (Convert(s) == Convert(s1))
Run Code Online (Sandbox Code Playgroud)

输出后:

(s, s1) => (s == s1)
(s, s1) => (s == s1)
Run Code Online (Sandbox Code Playgroud)

  • @MoranB呀,我不反对.但是,在没有任何规范的情况下,我能给你的唯一答案是"因为这是它的编码方式".尝试通过Roslyn运行它以查看Roslyn是否做同样的事情 - 实际上,也许是mcs/gmcs(单声道编译器)实际上很有趣.除此之外,我在想"责怪疯人"; p (2认同)

Rad*_*ler 5

回答你的问题:

为什么表达式树在处理字节和短路时似乎构建了不必要的转换...所以问题是,这种行为的原因是什么?

答案就藏在这样的事实,即C#类型short,ushort,bytesbyte缺少算术,比较...经营:

提取:4.1.5整数类型

对于二进制+, - ,*,/,%,&,^,|,==,!=,>,<,> =和<=运算符,操作数将转换为类型T,其中T第一个是int,uint,longulong那可以完全代表两个操作数的所有可能值.然后使用类型的精度执行操作T,结果的类型是T(或关系运算符的bool).不允许一个操作数为long类型,另一个操作数为binary类型的二元运算符.

7.9.1整数比较运营商介绍了可运营商和它们的操作数

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
Run Code Online (Sandbox Code Playgroud)

转换是由Compiler为您完成的(您在没有显式转换的情况下成功构建转换的原因)

因为没有运营商使用短...所以必须应用转换.当然,它后来依赖于LINQ提供程序,如何将这种"表达式"转换为SQL.