2013-05-28 21 views
24

Điều này trông giống như một lỗi trong việc nâng lên vô số toán hạng trên các cấu trúc chung.Tại sao các cấu trúc chung và phi chung được xử lý khác nhau khi xây dựng biểu thức nâng vận hành == thành vô hiệu?

Hãy xem xét các cấu trúc giả sau, đó sẽ ghi đè operator==:

Bây giờ xem xét các khái niệm sau:

Expression<Func<MyStruct, MyStruct, bool>> exprA = 
    (valueA, valueB) => valueA == valueB; 

Expression<Func<MyStruct?, MyStruct?, bool>> exprB = 
    (nullableValueA, nullableValueB) => nullableValueA == nullableValueB; 

Expression<Func<MyStruct?, MyStruct, bool>> exprC = 
    (nullableValueA, valueB) => nullableValueA == valueB; 

Cả ba biên dịch và chạy như mong đợi.

Khi họ đang biên soạn (sử dụng .Compile()) họ sản xuất các đoạn mã sau (diễn giải để tiếng Anh từ IL):

  1. Khái niệm đầu tiên mà chỉ mất MyStruct (không nullable) args, chỉ cần gọi op_Equality (việc triển khai của chúng tôi là operator ==)

  2. Biểu thức thứ hai, khi được biên dịch, tạo mã kiểm tra từng đối số để xem liệu nó có phải là HasValue hay không. Nếu cả hai không (cả hai đều bằng null), trả về true. Nếu chỉ có một giá trị, trả về false. Nếu không, hãy gọi số op_Equality trên hai giá trị.

  3. Biểu thức thứ ba kiểm tra đối số rỗng để xem nó có giá trị không - nếu không, trả về false. Nếu không, hãy gọi số op_Equality.

Cho đến nay rất tốt.

Bước tiếp theo: làm chính xác những điều tương tự với một kiểu generic - thay đổi MyStruct để MyStruct<T> ở khắp mọi nơi trong định nghĩa của các loại, và thay đổi nó để MyStruct<int> trong biểu thức.

Bây giờ biểu thức thứ ba biên dịch nhưng ném một ngoại lệ thời gian chạy InvalidOperationException với thông báo sau:

Các toán hạng cho nhà điều hành 'bình đẳng' không phù hợp với các thông số của phương pháp 'op_Equality'.

Tôi hy vọng các cấu trúc chung sẽ hoạt động chính xác giống như các cấu trúc không chung chung, với tất cả các nâng không có mô tả ở trên.

Vì vậy, câu hỏi của tôi là:

  1. Tại sao lại có một sự khác biệt giữa cấu trúc chung và không chung chung?
  2. Ý nghĩa của ngoại lệ này là gì?
  3. Đây có phải là lỗi trong C# /. NET không?

Mã đầy đủ để tái tạo này là available on this gist.

+1

Bạn cũng có thể đăng mã đã sửa đổi của mình không? Có vẻ như bạn có thể đã bỏ lỡ một điểm khi sao chép 'MyStruct ' thay cho 'MyStruct'. – dasblinkenlight

+1

Tôi đã thêm [gist] (https://gist.github.com/anonymous/5664417) bằng mã đầy đủ, cũng đã thêm một liên kết ở cuối câu hỏi. – sinelaw

Trả lời

22

Câu trả lời ngắn gọn là: có, đó là lỗi. Tôi đã đặt một repro tối thiểu và một phân tích ngắn dưới đây.

Lời xin lỗi của tôi. Tôi đã viết rất nhiều mã và vì vậy nó có khả năng xấu của tôi.

Tôi đã gửi bản giới thiệu cho nhóm quản lý phát triển, kiểm tra và chương trình Roslyn. Tôi nghi ngờ điều này tái tạo trong Roslyn, nhưng họ sẽ xác minh rằng nó không và quyết định liệu điều này làm cho thanh cho một gói dịch vụ C# 5.

Cũng vui lòng nhập sự cố trên connect.microsoft.com nếu bạn muốn nó cũng được theo dõi ở đó.


repro tối thiểu:

using System; 
using System.Linq.Expressions; 
struct S<T> 
{ 
    public static bool operator ==(S<T> a, S<T> b) { return false; } 
    public static bool operator !=(S<T> a, S<T> b) { return false; } 
} 
class Program 
{ 
    static void Main() 
    { 
     Expression<Func<S<int>?, S<int>, bool>> x = (a, b) => a == b; 
    } 
} 

Các mã được tạo ra trong repro tối thiểu tương đương với

ParameterExpression pa = Expression.Parameter(typeof(S<int>?), "a"); 
ParameterExpression pb = Expression.Parameter(typeof(S<int>), "b"); 
Expression.Lambda<Func<S<int>?, S<int>, bool>>(
    Expression.Equal(pa, pb, false, infoof(S<int>.op_Equality) 
    new ParameterExpression[2] { pa, pb }); 

đâu infoof là một nhà điều hành giả mà được một MethodInfo cho phương pháp đã cho.

Mã đúng sẽ là:

ParameterExpression pa = Expression.Parameter(typeof(S<int>?), "a"); 
ParameterExpression pb = Expression.Parameter(typeof(S<int>), "b"); 
Expression.Lambda<Func<S<int>?, S<int>, bool>>(
    Expression.Equal(pa, Expression.Convert(pb, typeof(S<int>?), false, infoof(S<int>.op_Equality) 
    new ParameterExpression[2] { pa, pb }); 

Phương pháp Equal không thể đối phó với một nullable, một toán hạng không nullable. Nó đòi hỏi cả hai đều là nullable hoặc không.

(Lưu ý rằng false là đúng này Boolean điều khiển cho dù kết quả của một sự bình đẳng dỡ bỏ là một Boolean dỡ bỏ;. Trong C# nó không phải là, trong VB nó được.)

+0

Một lần nữa, cảm ơn Eric. Tôi đã mở [lỗi kết nối] (https://connect.microsoft.com/VisualStudio/feedback/details/788793/expression-equal-with-one-nullable-and-one-plain-generic-struct-value -causes-invalidoperationexception #). – sinelaw

+10

Cho phép tôi cũng bình luận rằng không có phản hồi nhanh như thế này, nhóm của chúng tôi trong quá khứ sẽ chờ hàng tháng hoặc thậm chí nhiều năm để làm rõ về Connect, trong đó chúng tôi đã thực hiện các giải pháp có thể có hoặc không chính xác. Tôi hy vọng rằng ai đó vẫn ở Microsoft sẽ trả lời nhanh một chính sách chính thức. – sinelaw

+0

@sinelaw: Bạn được chào đón. Nhóm Roslyn xác nhận với tôi rằng điều này không repro trong C# 5 nhưng không repro trong Roslyn bằng cách này. Xem câu trả lời của Neal. –

5

Vâng, lỗi này đã biến mất trong Roslyn (trình biên dịch đang được phát triển). Chúng ta sẽ thấy về sản phẩm hiện có.

+0

Cảm ơn bạn đã trả lời! – sinelaw

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