How do the compiler know it is out of scope? - c#

Main()
{
int i =0;
...
...
while(true)
{
int k =0;
...
...
}
// K is out of scope..
}
How does the compiler know K is out of scope?

How does the compiler know [that a local variable] is out of scope?
First off, let's carefully define the terms you're using. The scope of a named entity is the region of program text in which it is legal to use the name of the entity without additional qualification of the name.
The scope of a local variable is defined by the specification as the region of program text which is the entire block that immediately contains the declaration.
The compiler determines the scope of the local variable by keeping track of a local declaration space associated with each syntactic block. When we need to resolve a name, we figure out what block the name usage is inside, and consult the related declaration space. Of course, blocks nest and so do local variable declaration spaces, so we might have to consult more than one, in order from inside-to-outside.
The actual data structures we use are straightforward hash tables optimized for fast lookup and filtering on various aspects needed by the compiler. (For example, we sometimes need to look up names but only want to get types, or only methods, and so on.)
Does that answer your question? It's a rather unclear question.

Because as the compiler processes the code it maintains information about each identifier it comes across and each scope it comes across and maintains boundaries for the latter. It knows that K was declared in the while scope and after the scope ends it probably marks the variable as 'no longer in scope' causing any use to be marked as an error.

k is out of scope because the block it was defined in is closed.

I would say it's a meaningless question. K is out of scope because you wrote the program that way: the compiler's entire function is to recognize and translate the programming language, including the lexical scope aspect of it.

Related

Variable in child scope preventing definition of variable in parent [duplicate]

if(true)
{
string var = "VAR";
}
string var = "New VAR!";
This will result in:
Error 1 A local variable named 'var'
cannot be declared in this scope
because it would give a different
meaning to 'var', which is already
used in a 'child' scope to denote
something else.
Nothing earth shattering really, but isn't this just plain wrong? A fellow developer and I were wondering if the first declaration should be in a different scope, thus the second declaration cannot interfere with the first declaration.
Why is C# unable to differentiate between the two scopes? Should the first IF scope not be completely separate from the rest of the method?
I cannot call var from outside the if, so the error message is wrong, because the first var has no relevance in the second scope.
The issue here is largely one of good practice and preventing against inadvertent mistakes. Admittedly, the C# compiler could theoretically be designed such that there is no conflict between scopes here. This would however be much effort for little gain, as I see it.
Consider that if the declaration of var in the parent scope were before the if statement, there would be an unresolvable naming conflict. The compiler simply does not differentiate between the following two cases. Analysis is done purely based on scope, and not order of declaration/use, as you seem to be expecting.
The theoretically acceptable (but still invalid as far as C# is concerned):
if(true)
{
string var = "VAR";
}
string var = "New VAR!";
and the unacceptable (since it would be hiding the parent variable):
string var = "New VAR!";
if(true)
{
string var = "VAR";
}
are both treated precisely the same in terms of variables and scopes.
Now, is there any actual reason in this secenario why you can't just give one of the variables a different name? I assume (hope) your actual variables aren't called var, so I don't really see this being a problem. If you're still intent on reusing the same variable name, just put them in sibling scopes:
if(true)
{
string var = "VAR";
}
{
string var = "New VAR!";
}
This however, while valid to the compiler, can lead to some amount of confusion when reading the code, so I recommend against it in almost any case.
isn't this just plain wrong?
No, this is not wrong at all. This is a correct implementation of section 7.5.2.1 of the C# specification, "Simple names, invariant meanings in blocks".
The specification states:
For each occurrence of a given
identifier as a simple-name in an
expression or declarator, within the
local variable declaration space
of that occurrence, every
other occurrence of the same
identifier as a simple-name in an
expression or declarator must refer to the same
entity. This rule ensures that the
meaning of a name is always the same
within a given block, switch block,
for-, foreach- or using-statement, or
anonymous function.
Why is C# unable to differentiate between the two scopes?
The question is nonsensical; obviously the compiler is able to differentiate between the two scopes. If the compiler were unable to differentiate between the two scopes then how could the error be produced? The error message says that there are two different scopes, and therefore the scopes have been differentiated!
Should the first IF scope not be completeley seperate from the rest of the method?
No, it should not. The scope (and local variable declaration space) defined by the block statement in the consequence of the conditional statement is lexically a part of the outer block which defines the body of the method. Therefore, rules about the contents of the outer block apply to the contents of the inner block.
I cannot call var from outside the if,
so the error message is wrong, because
the first var has no relevance in the
second scope.
This is completely wrong. It is specious to conclude that just because the local variable is no longer in scope, that the outer block does not contain an error. The error message is correct.
The error here has nothing to do with whether the scope of any variable overlaps the scope of any other variable; the only thing that is relevant here is that you have a block -- the outer block -- in which the same simple name is used to refer to two completely different things. C# requires that a simple name have one meaning throughout the block which first uses it.
For example:
class C
{
int x;
void M()
{
int x = 123;
}
}
That is perfectly legal; the scope of the outer x overlaps the scope of the inner x, but that is not an error. What is an error is:
class C
{
int x;
void M()
{
Console.WriteLine(x);
if (whatever)
{
int x = 123;
}
}
}
because now the simple name "x" means two different things inside the body of M -- it means "this.x" and the local variable "x". It is confusing to developers and code maintainers when the same simple name means two completely different things in the same block, so that is illegal.
We do allow parallel blocks to contain the same simple name used in two different ways; this is legal:
class C
{
int x;
void M()
{
if (whatever)
{
Console.WriteLine(x);
}
if (somethingelse)
{
int x = 123;
}
}
}
because now the only block that contains two inconsistent usages of x is the outer block, and that block does not directly contain any usage of "x", only indirectly.
This is valid in C++, but a source for many bugs and sleepless nights. I think the C# guys decided that it's better to throw a warning/error since it's, in the vast majority of cases, a bug rather than something the coder actually want.
Here's an interesting discussion on what parts of the specification this error comes from.
EDIT (some examples) -----
In C++, the following is valid (and it doesn't really matter if the outer declaration is before or after the inner scope, it will just be more interesting and bug-prone if it's before).
void foo(int a)
{
int count = 0;
for(int i = 0; i < a; ++i)
{
int count *= i;
}
return count;
}
Now imagine the function being a few lines longer and it might be easy to not spot the error. The compiler never complains (not it the old days, not sure about newer versions of C++), and the function always returns 0.
The behaivour is clearly a bug, so it would be good if a c++-lint program or the compiler points this out. If it's not a bug it is easy to work around it by just renaming the inner variable.
To add insult to injury I remember that GCC and VS6 had different opinions on where the counter variable in for loops belonged. One said it belonged to the outer scope and the other said it didn't. A bit annoying to work on cross-platform code. Let me give you yet another example to keep my line count up.
for(int i = 0; i < 1000; ++i)
{
if(array[i] > 100)
break;
}
printf("The first very large value in the array exists at %d\n", i);
This code worked in VS6 IIRC and not in GCC. Anyway, C# has cleaned up a few things, which is good.

About unassigned variables

Just curious, I'm not trying to solve any problem.
Why only local variables should be assigned?
In the following example:
class Program
{
static int a;
static int b { get; set; }
static void Main(string[] args)
{
int c;
System.Console.WriteLine(a);
System.Console.WriteLine(b);
System.Console.WriteLine(c);
}
}
Why a and b gives me just a warning and c gives me an error?
Addionally, why I can't just use the default value of Value Type and write the following code?
bool MyCondition = true;
int c;
if (MyCondition)
c = 10;
Does it have anything to do with memory management?
Tim gives a good answer to your first question but I can add a few more details.
Your first question is basically "locals are required to be definitely assigned, so why not make the same restriction on fields?" Tim points out that Jon points out that it is actually quite difficult to do so. With locals it is almost always crystal clear when a local is first read and when it is first written. In the cases where it is not clear, the compiler can make reasonable conservative guesses. But with fields, to know when a first read and a first write happens, you have to know all kinds of things about which methods are called in what order.
In short, analyzing locals requires local analysis; the analysis doesn't have to go past the block that contains the declaration. Field analysis requires global analysis. That's a lot of work; it's easier to just say that fields are initialized to their default values.
(Now, that said, it is of course possible to do this global analysis; my new job will likely involve doing precisely this sort of analysis.)
Your second question is basically "Well then, if automatic assignment of default values is good enough for fields then why isn't it good enough for locals?" and the answer is "because failing to assign a local variable and accidentally getting the default value is a common source of bugs." C# was carefully designed to discourage programming practices that lead to irritating bugs, and this is one of them.
Because other variables are initialized with their default value.
Jon Skeet has already found some interesting words on this issue:
For local variables, the compiler has a good idea of the flow - it can
see a "read" of the variable and a "write" of the variable, and prove
(in most cases) that the first write will happen before the first
read.
This isn't the case with instance variables. Consider a simple
property - how do you know if someone will set it before they get it?
That makes it basically infeasible to enforce sensible rules - so
either you'd have to ensure that all fields were set in the
constructor, or allow them to have default values. The C# team chose
the latter strategy.
and here's the related C# language specification:
5.3 Definite assignment
At a given location in the executable code of a function member, a
variable is said to be definitely assigned if the compiler can prove,
by a particular static flow analysis (ยง5.3.3), that the variable has
been automatically initialized or has been the target of at least one
assignment.
5.3.1 Initially assigned variables
The following categories of variables are classified as initially
assigned:
Static variables.
Instance variables of class instances.
Instance variables of initially assigned struct variables.
Array elements.
Value parameters.
Reference parameters.
Variables declared in a catch clause or a foreach statement.
5.3.2 Initially unassigned variables
The following categories of variables are classified as initially
unassigned:
Instance variables of initially unassigned struct variables.
Output parameters, including the this variable of struct instance
constructors.
Local variables, except those declared in a catch clause or a
foreach statement.
The CLR provides a hard guarantee that local variables are initialized to their default value. But this guarantee does have limitations. What is missing is its ability to recognize scope blocks inside the method body. They disappear once the compiler translates the code to IL. Scope is a language construct that doesn't have a parallel in the CLI and cannot be expressed in IL.
You can see this going wrong in a language like VB.NET for example. This contrived example shows the behavior:
Module Module1
Sub Main()
For ix = 1 To 3
Dim s As String
If ix = 2 Then s = "foo"
If s Is Nothing Then Console.WriteLine("null") Else Console.WriteLine(s)
Next
Console.ReadLine()
End Sub
End Module
Output:
null
foo
foo
Or in other words, the local variable s was initialized only once and retains its value afterwards. This has a knack for creating bugs of course. The VB.NET compiler does generate a warning for it and has simple syntax to avoid it (As New). A managed language like C++/CLI has the same behavior but doesn't emit a diagnostic at all. But the C# language gives a stronger guarantee, it generates an error.
This rule is called "definite assignment". The exact rules are explained in detail in the C# Language Specification, chapter 5.3.3
Definite assignment checking has its limitations. It works well for local variables since their scope is very limited (private to the method body) and you can't get to them with Reflection. Much harder to do with fields of a class, it requires whole-program analysis that may need to reach beyond what the compiler can see. Like code in another assembly. Which is why the C# compiler can only warn about it but can't reject the code out-right.

Do variables need to be referenced for them to be included in a closure?

When creating a closure (in Javascript or C#), are all variables in the scope at the time of the closure's creation "enclosed" in it? Or just the variables that are referenced in the newly created method?
Example C# code:
private void Main() {
var referenced = 1;
var notReferenced = 2; // Will this be enclosed?
new int[1].Select(x => referenced);
}
Example Javascript code:
var referenced = 1;
var notReferenced = 2; // Will this be enclosed?
var func = function () {
alert(referenced);
}
(Question came to me when reading about memory leaks in IE by creating circular references with Javascript closures. http://jibbering.com/faq/notes/closures/#clMem)
Note: with the word "enclosed", I mean what MSDN would call "captured". (http://msdn.microsoft.com/en-us/library/bb397687.aspx)
You have two questions here. In the future you might consider posting two separate questions when you have two questions.
When creating a closure in Javascript, are all variables in the scope at the time of the closure's creation "enclosed" in it?
You do not state which of many versions of "JavaScript" you are talking about. I'm going to assume you are talking about a correct implementation of the ECMAScript 3 language. If you are talking about some other version of "JavaScript", say which one you're talking about.
ECMAScript 5 has updated rules on how lexical environments and "eval" work. I have not been a member of Technical Committee 39 since 2001, and I have not been keeping up-to-date with recent changes to the ECMAScript 5 spec; if you want an answer in the context of recent ECMAScript 5 rules, find an expert on that specification; I'm not it.
The answer to your question in the context of ECMAScript 3 is yes. The ECMAScript 3 specification is very clear on this point, but you don't need to look at the spec to know that this must be the case:
function f()
{
var referenced = 1;
var notReferenced = 2;
var func = function ()
{
alert(referenced);
alert(eval("notReferenced"));
}
return func;
}
f()();
How can "eval" work correctly if "notReferenced" is not captured?
This is all explained in the ECMAScript specification, but I can briefly sum it up here.
Every variable is associated with a "variable object", which is the object that has properties whose names are the names of the variables. The variable object for the function f is identical to the activation object of f -- that is, the object that is magically created every time f is invoked. The variable object has three properties: "referenced", "notReferenced" and "func".
There is a variable object called the "global object" which represents the code outside of any function. It has a property "f".
Every execution context has a scope chain, which is the list of objects whose properties are searched when trying to evaluate an identifier.
Every function object has a scope chain associated with it, which is a copy of the scope chain that was in effect when the function was created.
The scope chain associated with "f" is the global object.
When execution enters "f", the current scope chain of the execution context has the activation object of "f" pushed onto it.
When "f" creates the function object assigned to "func", its associated scope chain is a copy of the current scope chain of the execution context -- that is, the scope chain that contains the activation of "f", and the global object.
OK, so now all the objects are set up correctly. f returns func, which is then invoked. That creates an activation object for that function. The scope chain of the execution context is fetched from the function object -- remember, it is the activation object of "f" plus the global object -- and onto that copy of the scope chain we push the current activation object. Since the anonymous function we are now executing has neither arguments nor locals, this is essentially a no-op.
We then attempt to evaluate "alert"; we look up the scope chain. The current activation and the "f" activation do not have anything called "alert", so we ask the global object, and it says yes, alert is a function. We then evaluate "referenced". It's not on the current activation object, but it is on f's activation object, so we fetch its value and pass it to alert.
Same thing on the next line. The global object tells us that there are methods "alert" and "eval". But of course "eval" is special. Eval gets a copy of the current scope chain as well as the string argument. Eval parses the string as a program and then executes the program using the current scope chain. That program looks up "notReferenced" and finds it because it is on the current scope chain.
If you have more questions about this area then I encourage you to read chapters 10 and 13 of the ECMAScript 3 specification until you thoroughly understand them.
Let's take a deeper look at your question:
When creating a closure in Javascript, are all variables in the scope at the time of the closure's creation "enclosed" in it?
To definitively answer that question you need to tell us precisely what you mean by "the closure" -- the ECMAScript 3 specification nowhere defines that term. By "the closure" do you mean the function object or the scope chain captured by the function object?
Remember, all these objects are mutable -- the scope chain itself is not mutable, but every object in the chain is mutable! Whether or not there was a variable "at the time" of the creation of either the scope chain or the function object is actually a bit irrelevant; variables come, variables go.
For example, variables that have not yet been created at the time of the function object's creation can be captured!
function f()
{
var func = function ()
{
alert(newlyCreated);
}
eval("var newlyCreated = 123;");
return func;
}
f()();
Clearly "newlyCreated" is not a variable at the time the function object is created because it is created after the function object. But when the function object is invoked, there it is, on f's activation/variable object.
Similarly, a variable that exists at the time a nested function object is created can be deleted by the time the function object is executed.
You have two questions here. In the future you might consider posting two separate questions when you have two questions.
When creating a closure in C#, are all variables in the scope at the time of the closure's creation "enclosed" in it? Or just the variables that are referenced in the newly created method?
It depends on whether you're asking for a de jure or a de facto answer.
First off, let's clarify your question. We define an "outer variable" as a local variable, value parameter (that is, not ref or out), "params" parameter, or "this" of an instance method that occurs inside a method but outside of any lambda or anonymous method expression that is inside that method. (Yes, "this" is classifed as an "outer variable" even though it is not classified as a "variable". That's unfortunate, but I've learned to live with it.)
An outer variable that is used inside of a lambda or anonymous method expression is called a "captured outer variable".
So now let's reformulate the question:
When creating the delegate at runtime that corresponds to a lambda or anonymous method, are the lifetimes of all the outer variables that are in scope at the time of the delegate's creation extended to match (or exceed) the lifetime of the delegate? Or are only the lifetimes of captured outer variables extended?
De facto, only the lifetimes of captured outer variables are extended. De jure, the spec requires the lifetimes of captured outer variables to be extended, but does not forbid the lifetimes of uncaptured outer variables to be extended.
Read sections 5.1.7, 6.5.1 and 7.15.5 of the C# 4 specification for all the details.
As Jon notes, we do not do a particularly good job of ensuring that lifetimes of captured outer variables are minimized. We hope to someday do better.
I can only answer on the C# side.
If the variable isn't actually captured by the closure, it will stay as a normal local variable in the method. It will only be hoisted to be an instance variable in a synthetic class if it's captured. (I'm assuming that's what you're talking about when you talk about variables being "enclosed".)
Note, however, that if two lambda expressions each capture different variables of the same scope, then in the current implementation both lambdas will use the same synthetic class:
private Action Foo() {
var expensive = ...;
var cheap = ...;
Action temp = () => Console.WriteLine(expensive);
return () => Console.WriteLine(cheap);
}
Here the returned action would still keep the "expensive" reference alive. All of this is an implementation detail of the Microsoft C# 4 compiler, however, and may change in the future.
I only can answer about C#:
No, it will not be enclosed, because it is not needed.
I interpret "will it be enclosed?" in this context like the following:
Will it be captured inside the closure that is generated for the lambda expression, i.e. will there be a field in the automatically generated class that contains the value of this variable?
Disclaimer:
This is an implementation detail. My answer is true for the current implementation of the C# compiler by MS for your specific case. It could be different with the Mono compiler or a newer version of the MS compiler. Or - as Jon's answer shows - it could be even different for a more complex example.
When creating a closure (in Javascript or C#), are all variables in the scope at the time of the closure's creation "enclosed" in it? Or just the variables that are referenced in the newly created method?
I can only answer about JavaScript.
This is implementation specific. Some engines enclose all variables. Some engines optimise and only enclose referenced variables.
I know Chrome optimises and only encloses the variables it cares about.
Further reading:
Closures and scope
V8 Closures implementation
It also seems the question originated from a concern about the old IE memory leak problem. This isn't an issue in modern browsers.
A good read on memory leaks that still exist in IE8 by making circular references between EcmaScript and Host objects :
IE8 memory leak
To clarify about the IE memory leak issues with closures, it's mainly about the circular references between Host objects (DOM) and JavaScript. So this issue does not exist unless you use the DOM (el.attachEvent or something similar)

C# variable scoping: 'x' cannot be declared in this scope because it would give a different meaning to 'x'

if(true)
{
string var = "VAR";
}
string var = "New VAR!";
This will result in:
Error 1 A local variable named 'var'
cannot be declared in this scope
because it would give a different
meaning to 'var', which is already
used in a 'child' scope to denote
something else.
Nothing earth shattering really, but isn't this just plain wrong? A fellow developer and I were wondering if the first declaration should be in a different scope, thus the second declaration cannot interfere with the first declaration.
Why is C# unable to differentiate between the two scopes? Should the first IF scope not be completely separate from the rest of the method?
I cannot call var from outside the if, so the error message is wrong, because the first var has no relevance in the second scope.
The issue here is largely one of good practice and preventing against inadvertent mistakes. Admittedly, the C# compiler could theoretically be designed such that there is no conflict between scopes here. This would however be much effort for little gain, as I see it.
Consider that if the declaration of var in the parent scope were before the if statement, there would be an unresolvable naming conflict. The compiler simply does not differentiate between the following two cases. Analysis is done purely based on scope, and not order of declaration/use, as you seem to be expecting.
The theoretically acceptable (but still invalid as far as C# is concerned):
if(true)
{
string var = "VAR";
}
string var = "New VAR!";
and the unacceptable (since it would be hiding the parent variable):
string var = "New VAR!";
if(true)
{
string var = "VAR";
}
are both treated precisely the same in terms of variables and scopes.
Now, is there any actual reason in this secenario why you can't just give one of the variables a different name? I assume (hope) your actual variables aren't called var, so I don't really see this being a problem. If you're still intent on reusing the same variable name, just put them in sibling scopes:
if(true)
{
string var = "VAR";
}
{
string var = "New VAR!";
}
This however, while valid to the compiler, can lead to some amount of confusion when reading the code, so I recommend against it in almost any case.
isn't this just plain wrong?
No, this is not wrong at all. This is a correct implementation of section 7.5.2.1 of the C# specification, "Simple names, invariant meanings in blocks".
The specification states:
For each occurrence of a given
identifier as a simple-name in an
expression or declarator, within the
local variable declaration space
of that occurrence, every
other occurrence of the same
identifier as a simple-name in an
expression or declarator must refer to the same
entity. This rule ensures that the
meaning of a name is always the same
within a given block, switch block,
for-, foreach- or using-statement, or
anonymous function.
Why is C# unable to differentiate between the two scopes?
The question is nonsensical; obviously the compiler is able to differentiate between the two scopes. If the compiler were unable to differentiate between the two scopes then how could the error be produced? The error message says that there are two different scopes, and therefore the scopes have been differentiated!
Should the first IF scope not be completeley seperate from the rest of the method?
No, it should not. The scope (and local variable declaration space) defined by the block statement in the consequence of the conditional statement is lexically a part of the outer block which defines the body of the method. Therefore, rules about the contents of the outer block apply to the contents of the inner block.
I cannot call var from outside the if,
so the error message is wrong, because
the first var has no relevance in the
second scope.
This is completely wrong. It is specious to conclude that just because the local variable is no longer in scope, that the outer block does not contain an error. The error message is correct.
The error here has nothing to do with whether the scope of any variable overlaps the scope of any other variable; the only thing that is relevant here is that you have a block -- the outer block -- in which the same simple name is used to refer to two completely different things. C# requires that a simple name have one meaning throughout the block which first uses it.
For example:
class C
{
int x;
void M()
{
int x = 123;
}
}
That is perfectly legal; the scope of the outer x overlaps the scope of the inner x, but that is not an error. What is an error is:
class C
{
int x;
void M()
{
Console.WriteLine(x);
if (whatever)
{
int x = 123;
}
}
}
because now the simple name "x" means two different things inside the body of M -- it means "this.x" and the local variable "x". It is confusing to developers and code maintainers when the same simple name means two completely different things in the same block, so that is illegal.
We do allow parallel blocks to contain the same simple name used in two different ways; this is legal:
class C
{
int x;
void M()
{
if (whatever)
{
Console.WriteLine(x);
}
if (somethingelse)
{
int x = 123;
}
}
}
because now the only block that contains two inconsistent usages of x is the outer block, and that block does not directly contain any usage of "x", only indirectly.
This is valid in C++, but a source for many bugs and sleepless nights. I think the C# guys decided that it's better to throw a warning/error since it's, in the vast majority of cases, a bug rather than something the coder actually want.
Here's an interesting discussion on what parts of the specification this error comes from.
EDIT (some examples) -----
In C++, the following is valid (and it doesn't really matter if the outer declaration is before or after the inner scope, it will just be more interesting and bug-prone if it's before).
void foo(int a)
{
int count = 0;
for(int i = 0; i < a; ++i)
{
int count *= i;
}
return count;
}
Now imagine the function being a few lines longer and it might be easy to not spot the error. The compiler never complains (not it the old days, not sure about newer versions of C++), and the function always returns 0.
The behaivour is clearly a bug, so it would be good if a c++-lint program or the compiler points this out. If it's not a bug it is easy to work around it by just renaming the inner variable.
To add insult to injury I remember that GCC and VS6 had different opinions on where the counter variable in for loops belonged. One said it belonged to the outer scope and the other said it didn't. A bit annoying to work on cross-platform code. Let me give you yet another example to keep my line count up.
for(int i = 0; i < 1000; ++i)
{
if(array[i] > 100)
break;
}
printf("The first very large value in the array exists at %d\n", i);
This code worked in VS6 IIRC and not in GCC. Anyway, C# has cleaned up a few things, which is good.

Why is the scope of if and delegates this way in c#

Inspired by this question I began wondering why the following examples are all illegal in c#:
VoidFunction t = delegate { int i = 0; };
int i = 1;
and
{
int i = 0;
}
int i = 1;
I'm just wondering if anyone knew the exact reason why the language was designed this way? Is it to discourage bad programming practice, and if so why not just issue a warning?, for performance reasons (compiling and when running) or what is the reason?
This behavior is covered in section 3 of the C# language specification. Here is the quote from the spec
Similarly, any expression that
occurs as the body of an anonymous
function in the form of a
lambda-expression creates a
declaration space which contains the
parameters of the anonymous function.
It is an error for two members of a
local variable declaration space to
have the same name. It is an error for
the local variable declaration space
of a block and a nested local variable
declaration space to contain elements
with the same name. Thus, within a
nested declaration space it is not
possible to declare a local variable
or constant with the same name as a
local variable or constant in an
enclosing declaration space.
I think the easier way to read this is that for the purpose of variable declaration (and many other block related functions) a lambda/anonymous delegate block are treated no different than a normal block.
As to why the the language was designed this way the spec does not explicitly state. My opinion though is simplicity. If the code is treated as just another block then it makes code analysis routines easier. You can preserve all of your existing routines to analyze the block for semantical errors and name resolution. This is particularly important when you consider variable lifting. Lambdas will eventually be a different function but they still have access to all in scope variables at the declaration point.
I think it is done this way so that the inner scope can access variables declared in the outer scope. If you were allowed to over-write variables existing in the outer scope, there may be confusion about what behavior was intended. So they may have decided to resolve the issue by preventing it from happening.
I think it's this way to prevent devs from shooting themselves in the foot.

Categories

Resources