2013-09-02 49 views
12

Cây biểu hiện dường như xây dựng một chuyển đổi không cần thiết khi làm việc với byte và quần short, chúng chuyển đổi cả hai mặt (trong biểu thức nhị phân chẳng hạn) thành int32.Cây biểu thức - chuyển đổi không cần thiết sang int32

Đây là sự cố trong một số nhà cung cấp LINQ mà tôi đã thấy, mỗi nhà cung cấp phải loại bỏ lớp thừa này để chuyển sang biểu thức gốc. (NHibernate không loại bỏ lớp này và tạo ra một CAST khủng khiếp trong truy vấn SQL).

// 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)); 

Nếu bạn cố gắng tạo biểu thức so sánh chính xác (không có chuyển đổi), bạn sẽ thành công.

Câu hỏi đặt ra là, lý do của hành vi này là gì?

EDIT .net 4.0 64bit, cùng áp dụng cho 4.5 64bit

+1

Phiên bản nào của trình biên dịch C# bạn đang làm việc cùng? Đoán duy nhất của tôi ở giai đoạn này sẽ là dọc theo dòng mà 'int' bình đẳng là một xây dựng trong hoạt động nguyên thủy trong. NET đó chỉ được định nghĩa cho' int' (loại ngắn hơn không thể tồn tại trên stack đánh giá) và đó là bằng cách nào đó một yếu tố đây. –

Trả lời

5

Đó là thực sự thú vị; Thật không may, các quy tắc của trình biên dịch biểu thức cây không được chỉ định chính thức - có một bản tóm tắt "là ở nơi khác" trong đặc tả, nhưng: chúng không thực sự.

Nếu nó đang gây ra một vấn đề, bạn có thể thử để phát hiện và loại bỏ nó - một cái gì đó như dưới đây, mà là 100% chưa được kiểm tra và sử dụng-at-riêng-rủi ro, vv:

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 trước:

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

Output sau:

(s, s1) => (s == s1) 
(s, s1) => (s == s1) 
+0

Cảm ơn câu trả lời chi tiết của bạn, nhưng câu hỏi là về ** tại sao ** đây là. Đây là một vấn đề phổ biến trong số các nhà cung cấp LINQ không có ý nghĩa ... – MoranB

+2

@MoranB yup, tôi không đồng ý. Tuy nhiên, trong trường hợp không có bất kỳ đặc điểm kỹ thuật câu trả lời duy nhất tôi có thể cung cấp cho bạn là "bởi vì đó là cách nó được mã hóa". Nó sẽ thực sự là thú vị để thử chạy nó thông qua Roslyn để xem liệu Roslyn làm điều tương tự - và thực sự, có lẽ mcs/gmcs (trình biên dịch mono). Khác hơn thế, tôi đang nghĩ "đổ lỗi cho Mads", p –

+1

@MarcGravell Tôi nhận được kết quả tương tự (với 'Chuyển đổi ') từ cả Roslyn và Mono. – svick

3

Để trả lời câu hỏi của bạn:

Tại sao Cây biểu thức dường như xây dựng một chuyển đổi không cần thiết khi làm việc với byte và ... Câu hỏi đặt ra là, lý do của hành vi này là gì?

Câu trả lời được ẩn trong thực tế, rằng C# loại short, ushort, bytesbyte thiếu số học, so sánh ... khai thác:

Extract: 4.1.5 Integral types

Đối các toán tử nhị phân +, -, *, /,%, &, ^, |, ==,! =,>, <,> = và < = , toán hạng được chuyển đổi sang loại T, nơi T là đầu tiên của int, uint, long, và ulong rằng hoàn toàn có thể đại diện cho tất cả giá trị có thể của cả hai toán hạng. Sau đó, thao tác được thực hiện bằng cách sử dụng độ chính xác của loại T và loại kết quả là T (hoặc bool cho các toán tử quan hệ ). Nó không được phép cho một toán hạng có kích thước là loại dài và loại kia là loại ulong với toán tử nhị phân.

Các 7.9.1 Integer comparison operators mô tả các nhà khai thác có sẵn và toán hạng của họ

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 

Việc chuyển đổi được thực hiện cho bạn bằng trình biên dịch (lý do tại sao bạn thành công để xây dựng mà không cần chuyển đổi rõ ràng)

Bởi vì có không có toán tử nào làm việc ngắn ... chuyển đổi phải được áp dụng. Và tất nhiên, nó sau này phụ thuộc vào nhà cung cấp LINQ, làm thế nào để chuyển đổi "biểu hiện" thành SQL.

Các vấn đề liên quan