This question already has answers here:
Is there a reason for C#'s reuse of the variable in a foreach?
(4 answers)
Closed 8 years ago.
Why the below code ouput 333 not 012?
I think the code is so simple and I check and check and double check, triple check, can not get answer yet. Any one can help me?
Action[] tmp = new Action[3];
for (int i = 0; i < tmp.Length; i++)
{
tmp[i] = () => Console.WriteLine(i);
}
Array.ForEach(tmp, m => m());
Console.Read();
You should change your code to:
Action[] tmp = new Action[3];
for (int i = 0; i < tmp.Length; i++)
{
int j = i;
tmp[i] = () => Console.WriteLine(j);
}
Array.ForEach(tmp, m => m());
Console.Read();
The reason is closure is nest
More detailed information please see those links:
Is there a reason for C#'s reuse of the variable in a foreach?
http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/
This occurs because there is only one i variable in the original code as for does not introduce a new variable "each loop" and the same variable is closed over in all the closures! As such, the same (i) variable is assigned the last value (3) before any of the functions are executed after the loop.
Compare with the following code which ensures a new variable to bind in each closure (this is one rare case where I use underscores for a local variable to show "something funny" is happening):
for (int _i = 0; _i < tmp.Length; _i++)
{
int i = _i; // NEW/fresh variable, i, introduced each loop
tmp[i] = () => Console.WriteLine(i);
}
This behavior (and complaints against) is discussed in detail in Is there a reason for C#'s reuse of the variable in a foreach? - for and foreach have this same "issue" in C#4 and before, which is "fixed" for foreach in C#5.
I think it is fair to say that all regret that decision. This is one of the worst "gotchas" in C#, and we are going to take the breaking change to fix it. In C# 5 the foreach loop variable will be logically inside the body of the loop, and therefore closures will get a fresh copy every time. - Eric Lippert
Related
This question already has answers here:
What does the '=>' syntax in C# mean?
(7 answers)
Closed 1 year ago.
I recently used a Parallel.For loop (see code below) in a small C# program and I'm a little perplexed by the ctr variable. Every example I've seen so far has the name of this variable set to ctr, but I cant seem to find any good resource on what it means or why exactly this name is used.
If anyone knows more about it, I would be happy to hear it!
public static int[] calcArray(int[] arrName, int arrSize, int seed)
{
Parallel.For(0, arrSize, ctr =>
{
arrName[ctr] = AllfunctionsClass.Random(seed);
seed++;
});
return arrName;
}
The current index value.. imagine a normal for loop
for (int ctr=0; ctr < arraySize; ctr++)
{
// ctr is the current value between 0 and arraySize-1
}
The variable name chosen as arbitrary in this case, probably short for counter. IMHO variable names should very rarely be abbrvted and should make it obvious what they represent e.g. arrayPosition or position or maybe index or something like that
This question already has answers here:
Regarding local variable passing in Thread
(2 answers)
Closed 2 years ago.
I'm working on a big list of buttons and trying to AddListener() for every Button on this list using a quick way
the first way is
btn[0] = CHbutt[0].GetComponent<Button>(); btn[0].onClick.AddListener(delegate { CH(0); });
btn[1] = CHbutt[1].GetComponent<Button>(); btn[1].onClick.AddListener(delegate { CH(1); });
btn[2] = CHbutt[2].GetComponent<Button>(); btn[2].onClick.AddListener(delegate { CH(2); });
btn[3] = CHbutt[3].GetComponent<Button>(); btn[3].onClick.AddListener(delegate { CH(3); });
and it works very well, and AddListener() to all Buttons in btn[];
but a lot of lines...
the second way
for (int i = 0; i < CHmatt.Count; i++)
{
btn[i] = CHbutt[i].GetComponent<Button>(); btn[i].onClick.AddListener(delegate { CH(i); });
}
but this one is not working, this one AddListener() to only the last button btn[0]
I'm very curious about the difference between these two scripts.
It's a capturing issue. Change the code as follows, to avoid capturing i:
for (int i = 0; i < CHmatt.Count; i++)
{
var ii = i;
btn[i] = CHbutt[i].GetComponent<Button>();
btn[i].onClick.AddListener(delegate { CH(ii); });
}
What is being passed into AddListener function is a delegate method; however, since you're passing in a loop iteration variable i, the context is captured, and i is actually referenced. As your loop advances, the value of i changes for all captures, therefore making this approach "not work".
By introducing a local copy of i (the name is not material here, I called it ii), which goes out of scope on each loop iteration, the capture of i is avoided, and the actual value of ii is passed to the delegate method (technically, captured, but it's a copy of i for each loop iteration, and therefore does not change as i changes.)
I'm using Task to process multiple requests in parallel and passing a different parameter to each task but it seems all the tasks takes one final parameter and execute the method using that.
Below is the sample code. I was expecting output as:
0 1 2 3 4 5 6 ..99
but I get:
100 100 100 ..10 .
May be before print method is called, i's value is already 100 but shouldn't each method print the parameter passed to it? Why would print method takes the final value of i?
class Program
{
static void Main(string[] args)
{
Task[]t = new Task[100];
for (int i = 0; i < 100; i++)
{
t[i] = Task.Factory.StartNew(() => print(i));
}
Task.WaitAll(t);
Console.WriteLine("complete");
Console.ReadLine();
}
private static void print(object i)
{
Console.WriteLine((int)i);
}
}
You're a victim of a closure. A simplest fix to this issue is:
for (int i = 0; i < 100; i++)
{
int v = i;
t[i] = Task.Factory.StartNew(() => print(v));
}
You can find more detailed explanations here and here.
Problems occur when you reference a variable without considering
its scope.
Task[]t = new Task[100];
for (int i = 0; i < 100; i++)
{
t[i] = Task.Factory.StartNew(() => print(i));
}
Task.WaitAll(t);
You might think that, your task will consider each i th value in it's execution. But that won't happen since Task execution start sometime in future. That means, the variable i is shared by all the closures created by the steps of the for loop. By the time the tasks start, the value of the single, shared variable i. This is why all task print same ith value.
The solution is to introduce an additional temporary variable in
the appropriate scope.
Task[]t = new Task[100];
for (int i = 0; i < 100; i++)
{
var temp=i;
t[i] = Task.Factory.StartNew(() => print(temp));
}
Task.WaitAll(t);
This version prints the numbers 1, 2, 3, 4..100 in an arbitrary order, but each
number will be printed. The reason is that the variable tmp is declared
within the block scope of the for loop’s body. This causes a new
variable named tmp to be instantiated with each iteration of the for
loop. (In contrast, all iterations of the for loop share a single instance
of the variable i.)
For info, another fix here is to use the state parameter of the Task API, i.e.
t[i] = Task.Factory.StartNew(state => print((int)state), i);
Unfortunately, since the state parameter is object, this still boxes the value, but it avoids needing an entire closure and separate delegate per call (with the code shown immediately above, the compiler is smart enough to use a single delegate instance for all the iterations; this is not possible if you add a local variable (like the v in BartoszKP's answer), as the target is the closure instance, and that then varies per iteration).
I'm using Task to process multiple requests in parallel and passing a different parameter to each task but it seems all the tasks takes one final parameter and execute the method using that.
Below is the sample code. I was expecting output as:
0 1 2 3 4 5 6 ..99
but I get:
100 100 100 ..10 .
May be before print method is called, i's value is already 100 but shouldn't each method print the parameter passed to it? Why would print method takes the final value of i?
class Program
{
static void Main(string[] args)
{
Task[]t = new Task[100];
for (int i = 0; i < 100; i++)
{
t[i] = Task.Factory.StartNew(() => print(i));
}
Task.WaitAll(t);
Console.WriteLine("complete");
Console.ReadLine();
}
private static void print(object i)
{
Console.WriteLine((int)i);
}
}
You're a victim of a closure. A simplest fix to this issue is:
for (int i = 0; i < 100; i++)
{
int v = i;
t[i] = Task.Factory.StartNew(() => print(v));
}
You can find more detailed explanations here and here.
Problems occur when you reference a variable without considering
its scope.
Task[]t = new Task[100];
for (int i = 0; i < 100; i++)
{
t[i] = Task.Factory.StartNew(() => print(i));
}
Task.WaitAll(t);
You might think that, your task will consider each i th value in it's execution. But that won't happen since Task execution start sometime in future. That means, the variable i is shared by all the closures created by the steps of the for loop. By the time the tasks start, the value of the single, shared variable i. This is why all task print same ith value.
The solution is to introduce an additional temporary variable in
the appropriate scope.
Task[]t = new Task[100];
for (int i = 0; i < 100; i++)
{
var temp=i;
t[i] = Task.Factory.StartNew(() => print(temp));
}
Task.WaitAll(t);
This version prints the numbers 1, 2, 3, 4..100 in an arbitrary order, but each
number will be printed. The reason is that the variable tmp is declared
within the block scope of the for loop’s body. This causes a new
variable named tmp to be instantiated with each iteration of the for
loop. (In contrast, all iterations of the for loop share a single instance
of the variable i.)
For info, another fix here is to use the state parameter of the Task API, i.e.
t[i] = Task.Factory.StartNew(state => print((int)state), i);
Unfortunately, since the state parameter is object, this still boxes the value, but it avoids needing an entire closure and separate delegate per call (with the code shown immediately above, the compiler is smart enough to use a single delegate instance for all the iterations; this is not possible if you add a local variable (like the v in BartoszKP's answer), as the target is the closure instance, and that then varies per iteration).
for (int i = 0; i < 10; i++)
{
Foo();
}
int i = 10; // error, 'i' already exists
----------------------------------------
for (int i = 0; i < 10; i++)
{
Foo();
}
i = 10; // error, 'i' doesn't exist
By my understanding of scope, the first example should be fine. The fact neither of them are allowed seems even more odd. Surely 'i' is either in scope or not.
Is there something non-obvious about scope I don't understand which means the compiler genuinely can't resolve this? Or is just a case of nanny-state compilerism?
By my understanding of scope, the first example should be fine.
Your understanding of scope is fine. This is not a scoping error. It is an inconsistent use of simple name error.
int i = 10; // error, 'i' already exists
That is not the error that is reported. The error that is reported is "a local variable named i cannot be declared in this scope because it would give a different meaning to i which is already used in a child scope to denote something else"
The error message is telling you what the error is; read the error message again. It nowhere says that there is a conflict between the declarations; it says that the error is because that changes the meaning of the simple name. The error is not the redeclaration; it is perfectly legal to have two things in two different scopes that have the same name, even if those scopes nest. What is not legal is to have one simple name mean two different things in nested local variable declarations spaces.
You would get the error "a local variable named i is already defined in this scope" if instead you did something like
int i = 10;
int i = 10;
Surely 'i' is either in scope or not.
Sure -- but so what? Whether a given i is in scope or not is irrelevant. For example:
class C
{
int i;
void M()
{
string i;
Perfectly legal. The outer i is in scope throughout M. There is no problem at all with declaring a local i that shadows the outer scope. What would be a problem is if you said
class C
{
int i;
void M()
{
int x = i;
foreach(char i in ...
Because now you've used i to mean two different things in two nested local variable declaration spaces -- a loop variable and a field. That's confusing and error-prone, so we make it illegal.
Is there something non-obvious about scope I don't understand which means the compiler genuinely can't resolve this?
I don't understand the question. Obviously the compiler is able to completely analyze the program; if the compiler could not resolve the meaning of each usage of i then how could it report the error message? The compiler is completely able to determine that you've used 'i' to mean two different things in the same local variable declaration space, and reports the error accordingly.
It is because the declaration space defines i at the method level. The variable i is out of scope at the end of the loop, but you still can't redeclare i, because i was already defined in that method.
Scope vs Declaration Space:
http://csharpfeeds.com/post/11730/Whats_The_Difference_Part_Two_Scope_vs_Declaration_Space_vs_Lifetime.aspx
You'll want to take a look at Eric Lippert's answer (who by default is always right concerning questions like these).
http://blogs.msdn.com/ericlippert/archive/2009/08/03/what-s-the-difference-part-two-scope-vs-declaration-space-vs-lifetime.aspx
Here is a comment from eric on the above mentioned post that I think talks about why they did what they did:
Look at it this way. It should always
be legal to move the declaration of a
variable UP in the source code so long
as you keep it in the same block,
right? If we did it the way you
suggest, then that would sometimes be
legal and sometimes be illegal! But
the thing we really want to avoid is
what happens in C++ -- in C++,
sometimes moving a variable
declaration up actually changes the
bindings of other simple names!
From the C# spec on local variable declarations:
The scope of a local variable declared
in a local-variable-declaration is the
block in which the declaration occurs.
Now, of course, you can't use i before it is declared, but the i declaration's scope is the entire block that contains it:
{
// scope starts here
for (int i = 0; i < 10; i++)
{
Foo();
}
int i = 10;
}
The for i variable is in a child scope, hence the collision of variable names.
If we rearrange the position of the declaration, the collision becomes clearer:
{
int i = 10;
// collision with i
for (int i = 0; i < 10; i++)
{
Foo();
}
}
Yea, I second the "nanny-state compilerism" comment. What's interesting is that this is ok.
for (int i = 0; i < 10; i++)
{
}
for (int i = 0; i < 10; i++)
{
}
and this is ok
for (int i = 0; i < 10; i++)
{
}
for (int j = 0; j < 10; j++)
{
var i = 12;
}
but this is not
for (int i = 0; i < 10; i++)
{
var x = 2;
}
var x = 5;
even though you can do this
for (int i = 0; i < 10; i++)
{
var k = 12;
}
for (int i = 0; i < 10; i++)
{
var k = 13;
}
It's all a little inconsistent.
EDIT
Based on the comment exchange with Eric below, I thought it might be helpful to show how I try to handle loops. I try to compose loops into their own method whenever possible. I do this because it promotes readability.
BEFORE
/*
* doing two different things with the same name is unclear
*/
for (var index = 0; index < people.Count; index++)
{
people[index].Email = null;
}
var index = GetIndexForSomethingElse();
AFTER
/*
* Now there is only one meaning for index in this scope
*/
ClearEmailAddressesFor(people); // the method name works like a comment now
var index = GetIndexForSomethingElse();
/*
* Now index has a single meaning in the scope of this method.
*/
private void ClearEmailAddressesFor(IList<Person> people)
{
for (var index = 0; index < people.Count; index++)
{
people[index].Email = null;
}
}
In the first example, the declaration of i outside of the loop makes i a local variable of the function. As a result, it is an error to have another variable name i declared within any block of that function.
The second, i is in scope only during the loop. Outside of the loop, i can no longer be accessed.
So you have seen the errors, but there is nothing wrong with doing this
for (int i = 0; i < 10; i++)
{
// do something
}
foreach (Foo foo in foos)
{
int i = 42;
// do something
}
Because the scope of i is limited within each block.
Or is just a case of nanny-state
compilerism?
Exactly that. There is no sense in "reusing" variable names in the same method. It's just a source of errors and nothing more.
Me thinks that the compiler means to say that i has been declared at the method level & scoped to within the for loop.
So, in case 1 - you get an error that the variable already exists, which it does
& in case 2 - since the variable is scoped only within the for loop, it cannot be accessed outside that loop
To avoid this, you could:
var i = 0;
for(i = 0, i < 10, i++){
}
i = 10;
but I can't think of a case where you would want to do this.
HTH
you need to do
int i ;
for ( i = 0; i < 10; i++)
{
}
i = 10;
class Test
{
int i;
static int si=9;
public Test()
{
i = 199;
}
static void main()
{
for (int i = 0; i < 10; i++)
{
var x = 2;
}
{ var x = 3; }
{ // remove outer "{ }" will generate compile error
int si = 3; int i = 0;
Console.WriteLine(si);
Console.WriteLine(Test.si);
Console.WriteLine(i);
Console.WriteLine((new Test()).i);
}
}
}