lambda functions have strange behaviors in loops, how to fix? [duplicate] - c#

This question already has answers here:
Can someone explain "access to modified closure" in C# in simple terms? [duplicate]
(1 answer)
Captured variable in a loop in C#
(10 answers)
Closed 5 years ago.
I get a really weird behavior with this piece of code
for (int i = 0; i < 2; ++i)
optionContent.GetChild(i).GetComponent<Button>().onClick.AddListener(() => SetOption(i, true));
The weird behavior is that all listeners gets attached the function SetOption(2, true)
I think the 2 comes from the fact that at the end of the for loop, i is set to 2, but still, why is this the behavior and is there a way to get around this?
Edit:
I played around for a bit and I think the problem is that delegates store variable references as references, not values
int x = 0;
Action a = delegate { int b = x; Console.WriteLine(b); };
x = 10;
a();
The above code will print 10, instead of 0. How do I make it to use the value 0?

Related

Correct way to change value in List<int> / array [duplicate]

This question already has answers here:
i = i++ doesn't increment i. Why? [duplicate]
(4 answers)
Closed 1 year ago.
Suppose the List/array
List<int> a = new List<int>(){1,2,3,4,5};
int []a = new int[]{1,2,3,4,5};
And I want to change the value at index 2, whats the correct way?
a[2]++; or a[2] = a[2]++;
Please explain the answer.
The result of a[2] = a[2]++ will be a[2], in this case 3.
The postfix increment a[2]++ will change the value of a[2] after the line in which it appears has finished.
If you want to change the value, then a[2]++ or a[2] = ++a[2] are valid.
The correct way is the first one:
A[2]++;
The second one also works but it does more work than necessary. It is equivalent to the following code:
a[2] = a[2]; // useless
a[2]++;
The suffix ++ operator (as in x++) applies after the line in which it appears has finished executing. The prefix ++ operator (as in ++x) first modifies the element and then uses the value.
I suggest you read the documentation about the "increment operator":
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/arithmetic-operators#increment-operator-

C# Windows Form - Apply filter to image normally but got NullReferenceException using Thread [duplicate]

This question already has answers here:
Captured variable in a loop in C#
(10 answers)
List Index Out of Range exception when creating a task
(3 answers)
Correct use of Parallel for loop in C#?
(1 answer)
Closed 2 years ago.
I'm applying a filter to multiple images, and it works well but really slow.
(NOTE: images, filter, filter_size, div are already loaded and assigned)
for (int i = 0; i < 5; i++)
out[i] = MyFiltering((Bitmap)images[i], filter, filter_size, div);
Therefore, I tried to use a Thread for each image filtering to speed up as below:
for (int i = 0; i < 5; i++)
{
FilterThread[i] = new Thread(() => {out[i] = MyFiltering((Bitmap)images[i], filter, filter_size, div)}
FilterThread[i].IsBackground = true;
FilterThread[i].Start();
}
I've tried different ways (e.g., ThreadPool, Task.Factory.StartNew), but always get the NullReferenceException in MyFiltering for input img (i.e., images[i]), which should be definitely NOT NULL!
Anyone has an idea?

C# for loop with Task array IndexOutOfRangeException [duplicate]

This question already has answers here:
IndexOutOfRangeException exception when using tasks in for loop in C#
(1 answer)
What is an IndexOutOfRangeException / ArgumentOutOfRangeException and how do I fix it?
(5 answers)
Closed 3 years ago.
im starting 10 tasks to get result from web api. Im get IndexOutOfRangeException on offsets array. But how its posible.
Actually 'i' variable canot be greater or equal that 10.
Can anyone help with this?
For loop not working corectly?
times = 10;
Task[] tasks = new Task[times];
int[] offsets = new int[times];
for (int i = 0; i < times; i++)
{
offsets[i] = offset;
tasks[i] = Task.Run(() => SearchLocalByQuery(query, offsets[i], (i + 1)));
offset += Limit;
}
Task.WaitAll(tasks);
i = 10,
i cannot be 10 in for loop off this example.
System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'
i cannot be 10 in for loop off this example.
It can be in the lambda expression. This expression:
() => SearchLocalByQuery(query, offsets[i], (i + 1))
captures the variable i, and will evaluate that variable value whenever it is executed. Task.Run returns immediately but won't necessarily have started executing the delegate yet - so it's entirely possible that the loop has moved onto the next iteration, or completed, before i is evaluated.
The simple fix for this is to declare a local variable to capture i inside the loop:
for (int i = 0; i < times; i++)
{
int copy = i;
offsets[i] = offset;
// Make sure we only use copy within the lambda expression
tasks[i] = Task.Run(() => SearchLocalByQuery(query, offsets[copy], copy + 1));
offset += Limit;
}
Now there'll be a fresh copy variable for each iteration of the loop, and that variable will never change its value - so the lambda expression will execute using "the value of i for that iteration of the loop" regardless of what the current value of i is.

C# increment a variable and convert to string on one line [duplicate]

This question already has answers here:
Pre- & Post Increment in C#
(6 answers)
Closed 5 years ago.
Simple request -- does anybody know a way to consolidate this to one line:
edit - I was NOT confused about pre vs post increment operators. The question I posted should have worked, I honestly do not know what happened. somewhere between VS and SO I fixed it.
startingMask++;
string mask2 = startingMask.ToString().PadLeft(3, '0');
Something like
string mask2 = (startingMask++).ToString().PadLeft(3, '0');
edit -- Alright -- chill out -- :)
Here is the final full solution, I should have provided more info in my first question, I was just looking for a nudge in the right direction (the number needed and starting number will be changing via a database pull at some point):
int startingMask = 76;
int numberNumberNeeded = 10;
List<string> masks = new List<string>();
while (numberNumberNeeded > 0)
{
string newMask = (startingMask++).ToString().PadLeft(3, '0');
masks.Add(newMask);
numberNumberNeeded--;
}
string mask2 = (++startingMask).ToString().PadLeft(3, '0');
Postfix ++ startingMask++ first take value and then increment value
Sufix ++ ++startingMask first increment value and then take value

Confusing thread behaviour [duplicate]

This question already has answers here:
creating new threads in a loop
(2 answers)
Closed 9 years ago.
In C#, if I execute
for (int i = 0;i < 10;i++)
new Thread(() => Console.Write(i)).Start();
I will possibly get 0223557799, that's strange, since i is a int, I think it should be copied before the thread starts.
Closures are your problem here.
Basically, instead of grabbing the value when you create the lambda (in the loop), it grabs it when it needs it. And computers are so fast that by the time that happens, it's already changed. It can't go through the whole loop, but it goes through some of it.
Here's a fix:
for (int i = 0; i < 10; i++)
{
var n = i;
new Thread(() => Console.Write(n)).Start();
}
Because Start() returns immediately, i++ happens before the thread gets a chance to print i to the console. I believe that a workaround is to create a local copy of the int, then print that:
for (int i = 0;i < 10;i++) {
int j = i;
new Thread(() => Console.Write(j)).Start();
}
What basically is happening is this:
You want to start a thread that prints the value of i.
The thread starts.
The code operating in the thread gets the value if i. Note that the value of i can be changed by now.
The value of i gets printed. But no guarantees to get a logical output.
Copy the value of i into another variable first and then print that value. The other answers provide enough samplecode.
Your lambda will be translated into set of the method and context class which will handle a refference to the i.
I would use the built in .NET parallelism support Task Parallelism
You won't have to worry about managing the threads it's done for you.
Example your code converted to the Parallelism libraries.
Parallel.For(0, 10, (i, state) =>
{
Console.WriteLine(i);
});

Categories