2
votes
int count = itemsToValidate.Count;
foreach(var item in itemsToValidate)
{
   item.ValidateAsync += (x, y) => this.HandleValidate(ref count);
}

private void HandleValidate(ref int x)
{
  --x;
  if (x == 0)
  {
       // All items are validated.
  }
}

For the above code resharper complained "Access to Modified Closure". Doesn't do that if I change that to type of object. Why is this a closure, even though I am passing by ref ?

1
in actual scenario there are asynchronous service calls, I want to know if all the items have been validated, so that I can let the user know.anivas
Maybe because Resharper sucks? (serious question, because your code runs fine)McGarnagle

1 Answers

4
votes

This happens all the time

ReSharper is warning you that count is implicitly captured by the lambdas that you are assigning as "validation complete" event handlers, and that its value may well change between the time the lambda is created (i.e. when you assign the event handler) and the time when it is invoked. If this happens, the lambda will not see the value one would intuitively expect.

An example:

int count = itemsToValidate.Count;
foreach(var item in itemsToValidate)
{
   item.ValidateAsync += (x, y) => this.HandleValidate(ref count);
}

// afterwards, at some point before the handlers get invoked:
count = 0;

In this instance the handlers will read the value of count as 0 instead of itemsToValidate.Count -- which might be called "obvious", but is surprising and counter-intuitive to many developers not familiar with the mechanics of lambdas.

And we usually solve it like this

The usual solution to "shut R# up" is to move the captured variable in an inner scope, where it is much less accessible and R# can be prove that it cannot be modified until the lambda is evaluated:

int count = itemsToValidate.Count;
foreach(var item in itemsToValidate)
{
   int inner = count; // this makes inner impossible to modify
   item.ValidateAsync += (x, y) => this.HandleValidate(ref inner);
}

// now this will of course not affect what the lambdas do
count = 0;

But your case is special

Your particular case is a comparatively rare one where you specifically want this behavior, and using the above trick would actually make the program behave incorrectly (you need the captured references to point to the same count).

The correct solution: disable this warning using the special line comments that R# recognizes.