2013-01-12 15 views
5

Tại sao expr1 biên dịch nhưng không phải expr2?Tại sao loại Biểu thức trong .NET cho phép xây dựng từ một hàm chứ không phải chuyển đổi từ một hàm?

Func<object> func =() => new object(); 
Expression<Func<object>> expr1 =() => new object(); 
Expression<Func<object>> expr2 = func; //Cannot implicitly convert type 'System.Func<object>' to 'System.Linq.Expressions.Expression<System.Func<object>>' 
+1

Rất nhiều lời giải thích tốt dưới đây, nhưng hãy nhớ bạn luôn có thể làm điều này nếu nó phù hợp với nhu cầu của bạn: 'Func func =() => đối tượng mới(); Biểu thức > expr2 =() => func(); ' – JLRishe

Trả lời

4

Một Func<T> là một đại biểu (một con trỏ tới một hàm), trong khi một Expression<Func<T>> là một cây biểu thức (một cấu trúc dữ liệu cây giống như mô tả một hoạt động). Sau đó bạn không thể gán cái này cho cái kia, vì chúng hoàn toàn khác nhau.

Khi bạn chỉ định hàm lambda trực tiếp cho một trình biên dịch Func<T>, hãy biên dịch mã cho hàm của bạn và gán con trỏ cho mã đã biên dịch, ví dụ: func. Mặt khác, khi bạn gán một hàm lambda trực tiếp vào một Expression<Func<T>> trình biên dịch sẽ xây dựng cây biểu thức (chỉ đơn giản là một thể hiện của một kiểu tham chiếu) và gán một tham chiếu đến đối tượng đó cho ví dụ. Ví dụ: . expr1. Đây chỉ là một tiện ích mà trình biên dịch cung cấp cho bạn, mang lại cho bạn một lựa chọn hấp dẫn hơn nhiều so với việc tự xây dựng cây biểu thức cho mình trong mã (tất nhiên điều này cũng hoàn toàn có thể).

+0

Nhưng dĩ nhiên - trình biên dịch thấy các loại khác nhau (không liên quan). Và tôi đoán trình biên dịch không thể "kiểm tra" 'func' để thấy rằng đó là một biểu thức lambda (bạn có thể có thể nói rằng tôi không phải là một nhà thiết kế trình biên dịch)? – Christian

+2

@Christian lambda không có loại CLR. Bạn không thể "lưu trữ" một lambda ở bất cứ đâu. Bạn có thể chuyển đổi nó thành cái gì khác nhưng sau đó nó không phải là một lambda nữa. Xem câu trả lời của tôi. – usr

0

System.Func<object>System.Linq.Expressions.Expression<System.Func<object>> là các loại khác nhau. Bạn không thể chuyển đổi chúng hoàn toàn.

Func<T> là đại biểu. Nhìn từ MSDN.

Đóng gói phương thức không có tham số và trả về giá trị loại được chỉ định bởi tham số T.

Expression<Func<T>> là cây biểu thức.

+0

Vậy tại sao expr1 là hợp pháp? – miniBill

+1

@miniBill Vì C# cho phép chuyển đổi ngầm từ một lambda thành một trong hai loại. – JLRishe

+1

Làm cho cảm giác, cảm ơn :) – miniBill

3

Bạn không thể chuyển đổi giữa các loại FuncExpression. Nhưng bạn có thể chuyển đổi từ một lambda sang một trong hai.

Một lambda có loại đặc biệt chỉ tồn tại trong hệ thống kiểu C#. CLR không biết lambda là gì. Chỉ trình biên dịch C# mới biết điều đó và các hoạt động duy nhất mà bạn có thể làm với một lambda là chuyển đổi thành các loại FuncExpression. (Điều này có nghĩa là bạn không thể gọi một lambda ví dụ như nó không có kiểu CLR nào cả).

Đó là lý do tại sao bạn quan sát "khoảng cách ngữ nghĩa" tại đây.

2

Nói cách đơn giản hơn các trình biên dịch có thể chuyển đổi một đoạn mã (Lambda expression) vào

  • một Expression
  • hay một Func

Điều này xảy ra ngay lập tức khi được sử dụng, sau đó mã đã biến mất, được chuyển đổi không thể đảo ngược. Nhưng FuncExpression là các loại hoàn toàn khác nhau.

Đó là lý do tại sao bạn không thể

Expression<Func<object>> expr2 = func; 

Nhưng ngược lại sẽ làm việc với

Func<object> func2 = expr1.Compile(); 
Các vấn đề liên quan