2013-09-02 3 views
12

표현 트리는 바이트와 단점을 사용하여 작업 할 때 불필요한 변환을 만드는 것처럼 보입니다. 두 표현식 (예 : 이진 표현식)이 int32로 변환됩니다.표현 트리 - 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 64 비트는 같은 4.5 정말 흥미로운 64 비트

+1

어떤 버전의 C# 컴파일러를 사용하고 있습니까? 이 단계에서 내 유일한 추측은'int' 평등은'int' (짧은 타입이 평가 스택 상에 존재할 수 없다)에 대해서 정의 된 .NET에서의 원시 연산의 빌드이며, 그것은 어떻게 든 인자가된다는 것입니다 이리. –

답변

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) 
+0

자세한 답변을 보내 주셔서 감사합니다.하지만 질문은 ** 이유 **입니다. 이것은 이해가 안되는 Linq 제공자들 사이에 일반적인 문제입니다 ... – MoranB

+2

@MoranB 그래, 나는 동의하지 않는다. 그러나 어떤 사양이 없으면 내가 줄 수있는 유일한 대답은 "그것이 코딩 된 방식이기 때문에"입니다. 실제로 Roslyn을 통해 Roslyn이 같은 일을하는지, 실제로 mcs/gmcs (모노 컴파일러)인지 여부를 확인하는 것은 흥미로운 일입니다. 그 외, 나는 "Mads 비난"이라고 생각하고있다; p –

+1

@MarcGravell Roslyn과 Mono에서 같은 결과를 얻고있다. – svick

3

가 귀하의 질문에 대답하려면 :

바이트와 ​​반바지로 작업 할 때

표현 나무 그래서 문제는,이 문제에 대한 이유 것입니다 ... 불필요한 변환을 구축하는 것?

추출 : 4.1.5 Integral types

를 들어 C#을 유형 short, ushort, bytesbyte는 산술, 비교 ... 운영자 부족

대답은 사실에 숨겨져 >, =,! =,>, 및 = < = 연산자와 피연산자 완전히 두 피연산자의 모든 가능한 값을 나타낼 수 Tint, uint, long의 제 이다 T, 및 ulong 유형으로 변환된다. 그런 다음 T 유형의 정밀도를 사용하여 연산을 수행하고 결과 유형은 T (또는 관계 연산자의 경우 bool)입니다. 하나의 피연산자가 유형 long이고 다른 하나가 2 진 연산자와 함께 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로 변환하는 방법이 달라집니다.