2011-10-31 15 views
5

Expression.Convert thường ném trong InvalidOperationException khi "Không có toán tử chuyển đổi nào được xác định giữa expression.Type và type."Expression.Convert không ném InvalidOperationException cho các tham số kiểu giá trị bất biến?

Thông số kiểu trả về của Func<> là biến thể cho các loại tham chiếu.

// This works. 
Func<SomeType> a =() => new SomeType(); 
Func<object> b = a; 

isn't covariant for value types.

Phương sai chỉ áp dụng cho các loại tham chiếu; nếu bạn chỉ định một loại giá trị cho một tham số kiểu biến thể, tham số kiểu đó là bất biến cho kiểu kết quả được tạo ra.

// This doesn't work! 
Func<int> five =() => 5; 
Func<object> fiveCovariant = five; 

Tuy nhiên, Expression.Convert tin rằng nó là tốt.

Func<int> answer =() => 42; 
Expression answerExpression = Expression.Constant(answer); 
// No InvalidOperationException is thrown at this line. 
Expression converted 
    = Expression.Convert(answerExpression, typeof(Func<object>)); 

Không InvalidOperationException bị ném khi gọi Expression.Convert. Cây biểu thức biên dịch chính xác, nhưng khi tôi gọi đại biểu được tạo, tôi nhận được một mong đợi InvalidCastException.

  1. Đây có phải là lỗi không? (I reported it as a bug on Microsoft Connect.)
  2. Làm thế nào để kiểm tra đúng loại có thể được chuyển đổi sang loại khác không? Some answers dường như đề cập đến việc sử dụng Convert. Tôi sẽ rất thích một phương pháp mà không phải sử dụng xử lý ngoại lệ như logic.

Có vẻ như toàn bộ logic phương sai không được hỗ trợ đúng cách. Đơn khiếu nại chính xác về việc không thể chuyển đổi từ Func<SomeType> thành Func<SomeOtherType>, nhưng không khiếu nại về việc chuyển đổi từ Func<object> thành Func<string>.

Điều thú vị là, một khi SomeTypeSomeOtherType là trong hệ thống phân cấp cùng lớp (SomeOtherType kéo dài từ SomeType), nó không bao giờ ném ngoại lệ. Nếu họ không, nó không.

Trả lời

7

Đây có phải là lỗi không?

Có. Thư viện cây biểu thức có lẽ không được cập nhật liên tục trong suốt khi chúng ta thêm hiệp phương sai và đối nghịch. Xin lỗi vì điều đó.

Tôi đã báo cáo lỗi này là lỗi trên Microsoft Connect.

Cảm ơn! Ai đó sẽ có một cái nhìn vào nó sau đó.

Làm cách nào để kiểm tra đúng loại có thể được chuyển đổi thành loại khác không?

Câu hỏi là mơ hồ. Với hai loại đối tượng, bạn có muốn biết:

  • Thời gian chạy .NET có nghĩ rằng các loại tương ứng được gán không?
  • Trình biên dịch C# có nghĩ rằng có một chuyển đổi tiềm ẩn giữa các loại?
  • Trình biên dịch C# có nghĩ rằng có một chuyển đổi rõ ràng giữa các loại?

"int" và "short" chẳng hạn không được gán tương thích theo quy tắc .NET. Int được chuyển đổi rõ ràng nhưng không chuyển đổi hoàn toàn thành ngắn, và ngắn gọn là cả hai ngầm định và rõ ràng có thể chuyển đổi thành int, theo các quy tắc C#.

+1

_ "Cho hai loại đối tượng, bạn có muốn biết" _: Như trong [tài liệu msdn trên Expression.Convert] (http://msdn.microsoft.com/en-us/library/bb292051.aspx): _ "Không có toán tử chuyển đổi nào được định nghĩa giữa expression.Type và type." _ Ngay bây giờ tôi không có cách nào để biết liệu cây biểu thức của tôi có biểu thị mã hợp lệ hay không, cho đến khi chạy nó. Vì mục đích của tôi, tôi cần phải biết liệu có thể chuyển đổi tiềm ẩn hay không. –

1

Nó không phải là lỗi. Expression.Convert đại diện cho một kiểm tra kiểu thời gian chạy, do đó, một InvalidCastException tại thời gian chạy sẽ là hành vi mong đợi.

Chỉnh sửa: điều đó không hoàn toàn chính xác. Nó không chính xác đại diện cho một kiểm tra kiểu thời gian chạy (Đây là tài liệu: http://msdn.microsoft.com/en-us/library/bb292051.aspx). Tuy nhiên, cây biểu thức được tạo ra tại thời gian chạy, vì vậy tất cả các loại kiểm tra phải xảy ra sau đó.

Chỉnh sửa: Tôi đang sử dụng .NET 4.0.

Bằng cách này, Convert không phàn nàn về chuyển đổi Func<object>-Func<string>vì chuyển đổi đó là đôi khi pháp. Nó là hợp pháp nếu Func<object> là một tham chiếu covariant đối với một đối tượng có kiểu thời gian chạy là Func<string>.Ví dụ:

Func<string> sFunc =() => "S"; 
Func<object> oFunc = sFunc; 
Func<string> anotherSFunc = (Func<string>)oFunc; 

Bây giờ, chuyển đổi quyết định có nên ném một InvalidOperationException bằng cách kiểm tra xem một loại có thể bị ép buộc với một loại khác hay không. Khi kiểm tra các đại biểu cho một chuyển đổi tham chiếu tiềm năng, có vẻ như mã không kiểm tra các tham số contravariant (đối số) và ném một InvalidOperationException nếu có bất kỳ loại giá trị nào. Nó dường như không làm điều đó kiểm tra tham số covariant (return type). Vì vậy, tôi bắt đầu nghi ngờ rằng điều này một lỗi, mặc dù tôi có xu hướng dự đoán về điều đó cho đến khi tôi có cơ hội để xem thông số kỹ thuật (xem Eric Lippert's Maybe there's something wrong with the universe, but probably not), mà tôi không có thời gian để làm ngay bây giờ.

+0

Chính xác, do đó, nó sẽ biết rằng nó không thể chuyển đổi 'Func 'thành' Func '? Ví dụ. nó biết nó không thể chuyển đổi 'Func 'thành' Func '. EDIT: Vì vậy, có vẻ như nó không biết rằng một trong hai. : O –

+0

Bất kỳ ai đã bỏ phiếu, hãy giải thích. – phoog

+0

@StevenJeuris Chính xác. Vấn đề là bất cứ điều gì Convert biết, nó chỉ có thể biết nó trong thời gian chạy. Trình biên dịch không thực hiện phân tích tĩnh. Vì vậy, bạn có thể gọi Chuyển đổi với tất cả các cách chuyển đổi bất hợp pháp và các cuộc gọi sẽ được biên dịch. Họ sẽ không thất bại cho đến khi mã được chạy. – phoog

0

Eric Lippert đã trả lời phần 1 câu hỏi của tôi: nó có vẻ là một lỗi. Tôi bắt đầu tìm kiếm giải pháp cho câu hỏi 2:

Làm cách nào để kiểm tra xem loại có thể được chuyển đổi thành loại khác không?

Tôi chỉ cam kết lần thử đầu tiên theo phương thức Type.CanConvertTo(Type to)to my library. (. Nguồn được cách quá phức tạp để trả lời ở đây, xin lỗi)

if (fromType.CanConvertTo(toType)) 
{ 
    convertedExpression = Expression.Convert(expression, toType); 
} 

Cho đến nay nó hỗ trợ kiểm tra chuyển đổi tiềm ẩn cho:

  • đơn giản loại phi generic.
  • Phương sai cho giao diện và đại biểu chung lồng nhau.
  • Invariance cho các thông số loại giá trị chung.

Nó không hỗ trợ:

  • Loại ràng buộc.
  • Toán tử chuyển đổi ngầm ẩn tùy chỉnh.

Nó vượt qua all my tests for implicit conversions. Mặc dù bạn có thể chỉ định để bao gồm các chuyển đổi rõ ràng là tốt (và tôi thực sự use it like that at the moment), tôi vẫn cần phải viết các bài kiểm tra đơn vị cho tất cả các kịch bản đó.