2015-05-07 16 views
6

Tôi đã đọc this answer và hiểu từ trường hợp cụ thể nó nổi bật, đó là khi bạn có một lambda bên trong lambda khác và bạn không muốn vô tình có lambda bên trong cũng biên dịch với bên ngoài. Khi bên ngoài được biên dịch, bạn muốn biểu thức lambda bên trong vẫn là một cây biểu thức. Có, có, nó có ý nghĩa trích dẫn biểu thức lambda bên trong.Tại sao bạn báo giá LambdaExpression?

Nhưng đó là về nó, tôi tin. Có trường hợp sử dụng nào khác để trích dẫn một biểu thức lambda?

Và nếu không, tại sao tất cả các nhà khai thác LINQ là, ví dụ: các phần mở rộng trên IQueryable<T> được tuyên bố trong đoạn trích Queryable lớp vị từ hoặc lambdas họ nhận được như các đối số khi họ đóng gói thông tin trong MethodCallExpression.

Tôi đã thử một ví dụ (và một vài người khác trong vài ngày qua) và dường như không có ý nghĩa gì khi báo một lambda trong trường hợp này.

Đây là biểu thức cuộc gọi phương thức cho phương thức dự kiến ​​biểu thức lambda (và không phải là cá thể đại diện) làm tham số duy nhất của nó.

Tôi sau đó biên dịch MethodCallExpression bằng cách gói nó bên trong một lambda.

Nhưng điều đó không biên dịch bên trong LambdaExpression (đối số cho phương pháp GimmeExpression). Nó để lại biểu thức lambda bên trong như một cây biểu thức và không tạo ra một cá thể đại biểu của nó.

Thực tế, nó hoạt động tốt mà không trích dẫn nó.

Và nếu tôi trích dẫn đối số, nó sẽ ngắt và cho tôi một lỗi chỉ ra rằng tôi đang chuyển sai loại đối số cho phương thức GimmeExpression.

Thỏa thuận là gì? Trích dẫn tất cả điều này là gì?

private static void TestMethodCallCompilation() 
{ 
    var methodInfo = typeof(Program).GetMethod("GimmeExpression", 
     BindingFlags.NonPublic | BindingFlags.Static); 

    var lambdaExpression = Expression.Lambda<Func<bool>>(Expression.Constant(true)); 

    var methodCallExpression = Expression.Call(null, methodInfo, lambdaExpression); 

    var wrapperLambda = Expression.Lambda(methodCallExpression); 
    wrapperLambda.Compile().DynamicInvoke(); 
} 

private static void GimmeExpression(Expression<Func<bool>> exp) 
{ 
    Console.WriteLine(exp.GetType()); 
    Console.WriteLine("Compiling and executing expression..."); 
    Console.WriteLine(exp.Compile().Invoke()); 
} 

Trả lời

4

Bạn phải vượt qua đối số là một ConstantExpression:

private static void TestMethodCallCompilation() 
{ 
    var methodInfo = typeof(Program).GetMethod("GimmeExpression", 
     BindingFlags.NonPublic | BindingFlags.Static); 

    var lambdaExpression = Expression.Lambda<Func<bool>>(Expression.Constant(true)); 

    var methodCallExpression = 
     Expression.Call(null, methodInfo, Expression.Constant(lambdaExpression)); 

    var wrapperLambda = Expression.Lambda(methodCallExpression); 
    wrapperLambda.Compile().DynamicInvoke(); 
} 

private static void GimmeExpression(Expression<Func<bool>> exp) 
{ 
    Console.WriteLine(exp.GetType()); 
    Console.WriteLine("Compiling and executing expression..."); 
    Console.WriteLine(exp.Compile().Invoke()); 
} 

Lý do nên được khá rõ ràng - bạn đang đi qua một giá trị không đổi, vì vậy nó có phải là một ConstantExpression. Bằng cách truyền trực tiếp biểu thức, bạn đang nói rõ ràng "và nhận giá trị của exp từ cây biểu thức phức tạp này". Và vì cây biểu thức đó không thực sự trả về giá trị Expression<Func<bool>>, bạn gặp lỗi.

Cách IQueryable hoạt động không thực sự có liên quan gì đến điều này. Các phương pháp mở rộng trên IQueryable phải bảo toàn tất cả thông tin về các biểu thức - bao gồm các loại và tham chiếu của ParameterExpression s và tương tự. Điều này là do chúng không thực sự là làm bất kỳ thứ gì - chúng chỉ xây dựng cây biểu thức. Công việc thực sự xảy ra khi bạn gọi queryable.Provider.Execute(expression). Về cơ bản, đây là cách đa hình được bảo tồn mặc dù chúng tôi đang làm thành phần, chứ không phải là thừa kế (/ thực hiện giao diện). Nhưng điều đó có nghĩa là các phương pháp mở rộng IQueryable không thể thực hiện bất kỳ lối tắt nào - chúng không biết gì về cách thức IQueryProvider thực sự sẽ giải thích truy vấn, vì vậy chúng không thể vứt bỏ bất kỳ thứ gì.

Lợi ích quan trọng nhất bạn nhận được từ điều này, tuy nhiên, là bạn có thể soạn truy vấn và truy vấn phụ.Hãy xem xét một truy vấn như thế này:

from item in dataSource 
where item.SomeRelatedItem.Where(subItem => subItem.SomeValue == 42).Count() > 2 
select item; 

Bây giờ, điều này được phiên dịch sang một cái gì đó như thế này:

dataSource.Where(item => item.SomeRelatedItem.Where(subItem => subItem.SomeValue == 42).Count() > 2); 

Truy vấn bên ngoài là khá rõ ràng - chúng tôi sẽ nhận được một Where với vị nhất định. Tuy nhiên, truy vấn bên trong thực tế sẽ là Call đến Where, lấy vị từ thực tế làm đối số.

Bằng cách đảm bảo rằng lời gọi thực tế của phương pháp Where đang thực sự chuyển thành Call của phương pháp Where, cả hai trường hợp trở nên giống nhau, và LINQProvider của bạn là niềm đam mê đó đơn giản hơn :)

Tôi đã thực sự viết các nhà cung cấp LINQ không triển khai IQueryable và thực tế có một số logic hữu ích trong các phương thức như Where. Nó đơn giản hơn nhiều và hiệu quả hơn, nhưng có nhược điểm được mô tả ở trên - cách duy nhất để xử lý các truy vấn phụ sẽ là các biểu thức Call theo cách thủ công để nhận biểu thức vị ngữ "thực". Yikes - đó là khá chi phí cho một truy vấn LINQ đơn giản! Và tất nhiên, nó giúp bạn soạn các nhà cung cấp truy vấn khác nhau, mặc dù tôi chưa thực sự thấy (m) bất kỳ ví dụ nào về việc sử dụng hai nhà cung cấp hoàn toàn khác nhau trong một truy vấn đơn lẻ.

Vì sự khác biệt giữa số Expression.ConstantExpression.Quote, chúng có vẻ khá giống nhau. Sự khác biệt quan trọng là Expression.Constant sẽ xử lý mọi đóng cửa là các hằng số thực tế hằng số, thay vì đóng cửa. Mặt khác, Expression.Quote sẽ giữ nguyên "đóng cửa" của các bao đóng. Tại sao? Bởi vì các đối tượng đóng cửa tự là cũng được thông qua là Expression.Constant :) Và kể từ IQueryable cây đang làm lambdas của lambdas lambdas của [...], bạn thực sự không muốn mất ngữ nghĩa đóng cửa tại bất kỳ điểm nào.

+0

Cảm ơn bạn rất nhiều. Tôi đã ở đây vài tháng rồi. Nó cuối cùng đã nhấp sau khi thực hiện rất nhiều ví dụ và suy nghĩ rất nhiều trong vài tháng qua về điều này. Tôi đã có một số giả thuyết về * tại sao * và một số trong số đó là đúng. Câu trả lời của bạn cũng đã giúp tôi. –

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