2012-05-08 51 views
63

Tôi đang bối rối về phạm vi của biến lambda, lấy ví dụ như sauPhạm vi của biến lambda trong C# là gì?

var query = 
    from customer in clist 
    from order in olist 
    .Where(o => o.CustomerID == customer.CustomerID && o.OrderDate == // line 1 
     olist.Where(o1 => o1.CustomerID == customer.CustomerID)  // line 2 
      .Max(o1 => o1.OrderDate)         // line 3 
    ) 
    select new { 
     customer.CustomerID, 
     customer.Name, 
     customer.Address, 
     order.Product, 
     order.OrderDate 
    }; 

Trong dòng 1 Tôi có khai báo một biến lambda 'o' có nghĩa là tôi không thể khai báo nó một lần nữa trong dòng 2 (hoặc ít nhất là trình biên dịch phàn nàn nếu tôi cố gắng) Nhưng nó không phàn nàn về dòng 3 mặc dù 'o1' đã tồn tại?

Phạm vi của biến lambda là gì?

+10

Bạn có câu trả lời bên dưới, tôi chỉ muốn chia sẻ một gợi ý đơn giản, thực tế: khi phạm vi không rõ ràng khi đọc mã và khi phạm vi quan trọng, hoặc tránh sự mơ hồ bằng cách sử dụng các tên biến khác nhau; hoặc viết ra lambdas của bạn dưới dạng dài, sử dụng dấu ngoặc nhọn, làm cho chúng trông giống như các hàm - tâm trí và mắt của hầu hết các lập trình viên tạo ra một sự phân biệt rõ ràng hơn khi bạn nhìn thấy các dấu ngoặc nhọn truyền thống. –

Trả lời

170

Các dấu ngoặc cung cấp cho các đầu mối - biến lambda được thể hiện trong phạm vi mà nó tuyên bố:

.Where(o => ... olist.Where(o1 => ...).Max(o1 => ...)) 
    // |----------------------------------------------| scope of o 
    //      |---------|     scope of first o1 
    //          |---------| scope of second o1 

Lưu ý rằng không có sự chồng chéo cho hai o1 biến, nhưng cả hai đều chồng chéo (hoặc bóng) sự o biến và do đó không thể sử dụng cùng một tên.

+58

+1 để hiển thị – Cheezmeister

+2

câu trả lời tuyệt vời ... cảm ơn dude! – mfc

+3

Câu trả lời hay nhất tôi đã thấy trên SO từ 3 năm trở lên. – kizzx2

4

becasue lamda là thay thế các chức năng ẩn danh ở đây trong bạn mã

Cùng Phạm vi

Where(o => o.CustomerID == customer.CustomerID && o.OrderDate == //line 1 
     olist.Where(o1 => o1.CustomerID == customer.CustomerID) //line 2  

là phạm vi chức năng, trong đó varialble "o" sống

đây trong dòng ba đây là scrop mới của biến tức là phạm vi chức năng mới

Phạm vi khác nhau

.Max(o1 => o1.OrderDate) )  //line 3 

vì vậy đó là reson trong line1 và dòng2 varialbe "o" quy định tại line1 không thể quy định tại dòng2 vì Scrop cùng và "o1" xác định trong dòng2 thể được định nghĩa một lần nữa trong Line3 becauase nó nằm trong phạm vi chức năng khác nhau

14

Phạm vi của tham số lambda bằng với toàn bộ phạm vi của nội dung của biểu thức lambda, bao gồm bất kỳ biểu thức lambda bên trong hoặc phạm vi nào.

Nếu chúng ta mở rộng cú pháp của biểu thức lambda của bạn và thêm một số vết lõm thân thiện với nó có thể trở nên rõ ràng hơn (mặc dù có lẽ không nơi nào rõ ràng như yamen's diagrammatic answer!):

.Where(o => { 
    return o.CustomerID == customer.CustomerID 
     && o.OrderDate == olist.Where(
      o1 => o1.CustomerID == customer.CustomerID 
     ) 
     .Max(
      o1 => o1.OrderDate 
     ); 
}) 

Thông báo rằng cuộc gọi .Where().Max() của bạn nằm trong một bên ngoài .Where(). Các o trong lambda bên ngoài được đóng gói bởi lambda bên ngoài bên trong lambdas bên trong của bạn (điều này được gọi là đóng cửa) sao cho nó tồn tại trong phạm vi của lambdas bên trong của bạn, và không thể được tái sử dụng như một tham số.

Bạn có thể sử dụng lại o1 vì hai lambda bên trong của bạn hoàn toàn tách biệt với nhau, vì vậy nó không nằm ngoài phạm vi của một trong hai.

2

C# không hỗ trợ đổ bóng như thế.

Lý do o1 hoạt động trở lại, là nó không che bóng trước o1.

5

Bạn không thể sử dụng cùng tên biến trong hai phạm vi nếu một trong các phạm vi có chứa phạm vi khác.

Trong câu hỏi của bạn, o được giới thiệu trong phạm vi bên ngoài, vì vậy nó không thể được sử dụng lại trong lần thứ hai Where() hoặc trong Max(), bởi vì những phạm vi được chứa trong một bên ngoài.

Mặt khác, bạn có thể sử dụng o1 ở cả hai phạm vi bên trong vì một không chứa phần khác, do đó không có sự mơ hồ ở đó.

2

Giống như bất kỳ biến nào khác. Phạm vi của o là toàn bộ biểu thức trong số Where đầu tiên của bạn, vì vậy bạn không thể sử dụng lại nó trong lần thứ hai, nằm bên trong biểu tượng đầu tiên. Nhưng phạm vi của o1 chỉ là biểu thức trong số Where thứ hai của bạn, vì vậy bạn có thể sử dụng nó trong biểu thức của Max, nằm ngoài số thứ hai Where. Trong đoạn mã:

// o scope lasts until the first bracket is closed 
Where(o => o.CustomerID == customer.CustomerID && o.OrderDate == 
// o1 scope lasts until the second bracket is closed; the first is not yet closed here 
     olist.Where(o1 => o1.CustomerID == customer.CustomerID) 
// The second bracket is closed, so o1 is already out of scope; o is still in scope 
      .Max(o1 => o1.OrderDate) 
) 
// The first bracket is closed, so o is finally out of scope 
2

tôi cố gắng hình nó như thế này theo mã của bạn ...

.Where(o => o.CustomerID == customer.CustomerID && o.OrderDate == // line 1 
     olist.Where(o1 => o1.CustomerID == customer.CustomerID)  // line 2 
      .Max(o1 => o1.OrderDate)         // line 3 
    ) 

cách thô Thay giải thích nhưng dấu ngoặc bạn xác định phạm vi. o1 thứ 2 của bạn không được lồng vào bên trong thứ hai ở đâu, khác khôn ngoan bạn phải cùng một vấn đề

//outermost where 

((BEGIN-o 

//inner where 

(BEGIN-o1 END-o1) 

//max 

(BEGIN-o1 END-o1) 

END-o)) 
1
var external = 1; 

//This is a scope 
Action scope = new Action(() => 
{ 
    //myVar is not accessible from outside 
    var myVar = 0 + external; 
    Console.WriteLine(myVar); //outputs 1 
}); 

//Call the scope 
scope(); 
//Console.WriteLine(myVar);//Will not compile 

Khi mã được biên dịch, tất cả các logic từ khoảng trống khai báo trong phần Action ()=>{ ... } sẽ đã chuyển sang phương thức trên loại có tên bị xé.

Thời gian chạy sẽ gọi hàm mới được tạo khi nó đạt đến vị trí đó trên ngăn xếp.

Bạn có thể chuyển giá trị vào một phạm vi/lambda theo nhiều cách khác nhau giống với cách đưa chúng ra.

Các biến được khai báo trong một lambda không thể truy cập được bên ngoài với tên được khai báo của chúng.

Bạn cũng có thể sử dụng sự phản chiếu để lấy tên bị xé tuy nhiên tôi không chắc bạn có yêu cầu điều đó hay không. (Vui lòng cho tôi biết nếu tôi sai.)

+0

Câu trả lời của tôi đơn giản hơn để đọc và tổng quát hơn Các mục đích khác không hiển thị phạm vi hoặc bạn không thể lặp lại tên trong phạm vi, ví dụ: (o => o.Somethings.Where (o => o.IsTrue)) // Lỗi vì o là đã được sử dụng. – Jay

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