Code Inspection: Access to modified captured variable
First of all, let's make sure that you understand what a closure is. To put it simply, a closure in C# is a lambda expression or an anonymous method that captures some variables from an outer scope. Here is the simplest example:
In the example above, print
will capture the variable myStr
(and not its value) and will only get the value of myStr
when you invoke print()
.
In more complex scenarios, when a closure is defined in a changing context, it may not behave as expected.
Here is an example of defining the above closure inside a loop:
Surprisingly, this code produces an ArgumentOutOfRangeException
when we invoke myActions[0]();
. The following happens here: instead of executing Console.WriteLine(myStrings[0]);
, which may seem intuitive, this call tries to execute Console.WriteLine(myStrings[i]);
and because i
is scoped to the whole for
cycle, its value not equals 0
, and even not 2
(which was the last time the condition was true). As the result of the last ++
operation the value became 3
just before the condition became false and we exited the loop. As myStrings
only has 3 elements, myStrings[3]
leads to the ArgumentOutOfRangeException
.
Although JetBrains Rider doesn't infer the consequence, which takes the shape of the ArgumentOutOfRangeException
here, it correctly points to the source of the problem — the iteration variable in closure — and suggests to fix it by copying the value of a changing variable to the scope where the closure is defined:
This fix makes sure that when you pick an action from myActions
and get the context where this action was created, i1
will hold the value corresponding to the index of the action in the list.