2012-05-08 4 views
63

Я запутался о масштабах переменной лямбда, взять, например, следующиеКакова область применения лямбда-переменной в C#?

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 
    }; 

В строке 1 я должен объявить переменную лямбда «O», которое означает, что я не могу объявить его снова в строке 2 (или по крайней мере, компилятор жалуется, если я пытаюсь) Но он не жалуется на строку 3, хотя «o1» уже существует?

Какова область лямбда-переменной?

+10

У вас есть ответ ниже, я просто хотел бы поделиться простым практическим советом: когда область видимости не понятна, прочитав код, и когда область действия важна, либо избегать визуальной двусмысленности, используя разные имена переменных; или выпишите свои лямбды в длинной форме, используя фигурные скобки, делая их больше похожими на функции - ум и глаза большинства программистов делают более четкое различие, когда вы видите традиционные фигурные скобки. –

ответ

170

Кронштейны дают ключ - переменная лямбда захватываются в объеме, где она объявлена:

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

Следует отметить, что нет никакого совпадения для двух o1 переменных, но оба они перекрываются (или теневой) o и, следовательно, не может использовать одно и то же имя.

+58

+1 для визуализации – Cheezmeister

+2

отличный ответ ... спасибо чувак! – mfc

+3

Лучший ответ, который я видел на SO с 3 лет. – kizzx2

4

еще потому LAMDA является замена анонимной функции здесь в вас код

же Scope

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

является сфера функции, в которой varialble "о" живой

здесь, в третьей строке это новая шкала переменной, т. е. новая функциональная область

Различные Scope

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

так что это Резон в линии 1 и line2 varialbe «о», определенной в LINE1 не определено в line2 из-за того же scrop и «o1» определяют в line2 может быть определена снова в Line3 becauase него находится в разном диапазоне функций

14

Объем параметра лямбда равен всей области тела лямбда-выражения, включая любые внутренние лямбда-выражения или области.

Если расширить синтаксис ваших лямбда-выражений и добавить дружеский отступа может стать понятнее (хотя, вероятно, нигде так ясно, как yamen's diagrammatic answer!):

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

Обратите внимание, что ваш .Where().Max() вызов находится внутри внешнего .Where(). o во внешней лямбда инкапсулируется внешней лямбдой внутри ваших внутренних лямбдов (это называется закрытием), так что оно существует в области ваших внутренних лямбдов уже и не может быть повторно использовано в качестве параметра.

Вы можете повторно использовать o1, потому что ваши две внутренние лямбды полностью отделены друг от друга, поэтому они не выходят за рамки любого из них.

2

C# не поддерживает затенение, подобное этому.

Причина, по которой o1 работает снова, заключается в том, что она не сглаживает предыдущие o1.

5

Вы не можете использовать одно и то же имя переменной в двух областях, если одна из областей содержит другую.

В вашем вопросе, o вводится во внешней области видимости, поэтому он не может быть использован повторно во втором Where() или в Max(), потому что эти объемы содержатся в наружной.

С другой стороны, вы можете использовать o1 в обеих внутренних областях, потому что один не содержит другого, поэтому там нет никакой двусмысленности.

2

Это то же самое, что и с любой другой переменной. Объем o - это все выражение в вашем первом Where, поэтому вы не можете использовать его снова во втором, который находится внутри первого. Но область o1 - это просто выражение в вашем втором Where, поэтому вы можете использовать его в выражении своего Max, который находится за пределами второго Where. В коде:

// 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

Стараюсь картину это так, как в вашем коде ...

.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 
    ) 

Вместо сырой способ объяснения, но ваши скобки определяют сферу. Ваш второй o1 не вложен внутри второго, где другие мудрые вы бы иметь такую ​​же проблему

//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 

При компиляции кода, вся логика из пустоты, объявленной в действии ()=>{ ... } будет переместился в метод с типом с искаженным именем.

Время выполнения вызовет вновь созданную функцию, когда она достигнет этого места в стеке.

Вы можете передавать значения в область/лямбда различными способами, которые одинаковы для их получения.

Переменные, объявленные в лямбде, не доступны снаружи с их объявленным именем.

Также можно использовать отражение, чтобы вытащить изможденное имя, но я не уверен, что вы этого требуете. (Пожалуйста, дайте мне знать, если я ошибаюсь.)

+0

Мой ответ проще читать и более общий с целью ответа на вопрос. Другие не отображают область действия или что вы не можете повторять имена в области видимости, например (o => o.Somethings.Where (o => o.IsTrue)) // Ошибка, потому что o уже в использовании. Я заслуживаю больше репутации. – Jay

Смежные вопросы