表达式树在处理字节和短路时似乎构建了不必要的转换,它们将双方(例如二进制表达式)转换为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));
如果您尝试构建一个能够进行精确比较的表达式(没有转换),那么您将获得成功。
所以问题是,这种行为的原因是什么?
修改 .net 4.0 64bit,同样适用于4.5 64bit
答案 0 :(得分: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);
}
}
之前的输出:
(s, s1) => (Convert(s) == Convert(s1))
(s, s1) => (Convert(s) == Convert(s1))
输出之后:
(s, s1) => (s == s1)
(s, s1) => (s == s1)
答案 1 :(得分:5)
回答你的问题:
为什么在处理字节和短路时,表达式树似乎构建了不必要的转换... 所以问题是,这种行为的原因是什么?
答案隐藏在以下事实中: C#类型short
,ushort
,byte
和sbyte
缺少算术,比较...运算符< /强>:
对于二进制+, - ,*,/,%,&amp;,^,|,==,!=,&gt;,&lt;,&gt; =和&lt; = 运算符,操作数转换为
T
类型,其中T
是第一个 可以完全代表所有可能的int
,uint
,long
和ulong
两个操作数的值。然后使用该操作执行操作 类型为T
的精度,结果的类型为T
(或者为 关系运算符)。一个操作数不允许 使用二元运算符输入long,另一个类型为ulong。
7.9.1 Integer comparison operators描述了可用的运算符及其操作数
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
编译器为您完成了转换(无需显式转换即可成功构建转换的原因)
因为没有运营商使用短...所以必须应用转换。当然,它后来依赖于LINQ提供程序,如何将这种“表达式”转换为SQL。