2012-05-08 9 views
63

Ich bin verwirrt über den Umfang des Lambda-Variable, nehmen Sie zum Beispiel des folgendWelchen Umfang hat eine Lambda-Variable in 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 
    }; 

In Zeile 1 Ich habe ein Lambda-Variable ‚o‘ erklären, das bedeutet, dass ich es nicht erklären kann wieder in Zeile 2 (oder zumindest beschwert sich der Compiler, wenn ich versuche) Aber es beschwert sich nicht über Zeile 3, obwohl 'o1' bereits existiert ??

Welchen Umfang hat eine Lambda-Variable?

+10

Sie haben die Antwort unten, ich möchte nur einen einfachen, praktischen Hinweis teilen: Wenn der Umfang nicht klar aus dem Code zu lesen ist, und wenn der Umfang zählt, entweder die visuelle Ambiguität vermeiden, indem man verschiedene Variablennamen verwendet; oder schreiben Sie Ihre lambdas in langen form, mit geschweiften Klammern, so dass sie mehr wie Funktionen aussehen - der Verstand und das Auge der meisten Programmierer machen eine klarere Unterscheidung, wenn Sie die traditionellen geschweiften Klammern sehen. –

Antwort

170

Die Klammern geben den Hinweis - die Lambda-Variable im Rahmen erfasst wird, in dem es deklariert wird:

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

Beachten Sie, dass keine Überlappung für die beiden o1 Variablen gibt es, aber sie beide überlappen (oder Schatten) die o Variable und kann daher nicht den gleichen Namen verwenden.

+58

+1 für Visualisierung – Cheezmeister

+2

tolle Antwort ... danke Kumpel! – mfc

+3

Beste Antwort, die ich seit 3 ​​Jahren auf SO gesehen habe. – kizzx2

4

becasue Lamda ist Ersatz der anonymen Funktion hier in Sie Code

Same Scope

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

ist Anwendungsbereich der Funktion, in der varialble "o" live

hier in Zeile drei Dies ist ein neuer Vorteil der Variablen, dh neuer Funktionsumfang

Verschiedene Scope

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

so ist, dass die reson in line1 und line2 varialbe "o" definiert in line1 kann nicht wegen der gleichen Scrop in line2 definiert und "o1" definieren in line2 kann in line3 becauase es wieder definiert werden ist in einem anderen Funktionsumfang

14

Der Bereich eines Lambda-Parameters entspricht dem gesamten Bereich des Körpers des Lambda-Ausdrucks, einschließlich aller inneren Lambda-Ausdrücke oder Bereiche.

Wenn wir die Syntax der Lambda-Ausdrücke erweitern und einige freundliche Vertiefung hinzuzufügen klarer werden kann (wenn auch wahrscheinlich nirgends so klar wie yamen's diagrammatic answer!):

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

Beachten Sie, dass Ihre .Where().Max() Anruf innerhalb einer äußeren befindet .Where(). Die o im äußeren Lambda ist durch das äußere Lambda innerhalb Ihres inneren Lambdas (das heißt Verschluss) so eingekapselt, dass es bereits im Bereich Ihrer inneren Lambdas existiert und nicht als Parameter wiederverwendet werden kann.

Sie können o1 wiederverwenden, weil Ihre zwei inneren Lambdas vollständig voneinander getrennt sind, so dass es nicht über den Rahmen beider hinausgeht.

2

C# unterstützt kein Shadowing.

Der Grund o1 funktioniert wieder, ist, dass es die vorherige o1 nicht überschattet.

5

Sie können denselben Variablennamen nicht in zwei Bereichen verwenden, wenn einer der Bereiche den anderen enthält.

In Ihrer Frage, o ist im äußeren Umfang eingeführt, so kann es nicht wieder in den zweiten Where() oder in Max() verwendet werden, da diese Bereiche in dem äußeren enthalten sind.

Auf der anderen Seite können Sie o1 in beiden inneren Bereichen verwenden, da der eine den anderen nicht enthält, also gibt es dort keine Zweideutigkeit.

2

Es ist das gleiche wie bei jeder anderen Variable. Der Umfang von o ist der gesamte Ausdruck in Ihrer ersten Where, so dass Sie es nicht wieder in der zweiten, die in der ersten ist, verwenden können. Aber der Umfang von o1 ist nur der Ausdruck in Ihrem zweiten Where, so dass Sie es in den Ausdruck Ihrer Max verwenden können, die außerhalb der zweiten Where ist. Im Code:

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

Ich versuche Bild es so, wie pro Ihren Code ...

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

eher grobe Art und Weise zu erklären, aber Ihre Klammern bestimmen den Umfang. Ihre zweite o1 ist nicht innerhalb des zweiten verschachtelt, wo andere weise Sie das gleiche Problem

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

haben würde, wenn der Code kompiliert wird, die gesamte Logik aus dem Nichts in der Klage, die als ()=>{ ... } wird verschoben zu einer Methode auf dem Typ mit einem verkrüppelten Namen.

Die Laufzeit wird die neu erstellte Funktion aufrufen, wenn sie diese Stelle auf dem Stapel erreicht.

Sie können Werte in einem Bereich/Lambda auf verschiedene Arten übergeben, die für das Aussteigen gleich sind.

Variablen, die in einem Lambda deklariert sind, sind mit ihrem deklarierten Namen nicht von außen zugänglich.

Es ist auch möglich, Reflektion zu verwenden, um den verfälschten Namen herauszuziehen, aber ich bin nicht sicher, dass Sie das benötigen. (Bitte lassen Sie mich wissen, wenn ich falsch liege.)

+0

Meine Antwort ist einfacher zu lesen und allgemeiner zum Zweck der Beantwortung der Frage, die anderen zeigen nicht den Umfang oder dass Sie Namen in einem Bereich nicht wiederholen können, zB (o => o.Somethings.Where (o => o.IsTrue)) // Fehler, weil o ist schon in Benutzung, ich verdiene mehr Rep. – Jay

Verwandte Themen