views:

106

answers:

3

I have the latest ReSharper 5.0 build (1655), where I have encountered the suggestion 'Access to modified closure' on the following code:

var now = new DateTime(1970, 1, 1);
var dates = new List<DateTime>();
dates.Where(d => d > now);
...
now = new DateTime();

and the now inside the lambda expression is underlined with the warning.

I'm pretty sure that's a ReSharper bug, but is it really?

EDIT: I should have checked better, there was an assignment to now later in the code.

EDIT 2 Jon Skeet's answer below pretty much answers this, but what about the following:

var query = dates.Where(d => d > now).ToList();

Shouldn't this solve the problem by executing the query immediately?

A: 

No this is resharper thinking you are modifying the contents of a collection while looping over it, dates most probably. You can safely ignore this.

Gerrie Schenck
Since `now` is a captured variable any changes to it before query is evaluated will most likely yield a different result than expected, which is what R# is trying to highlight.
Brian Rasmussen
-1: he cannot safely ignore it
ANeves
hmm apparently the question was changed to code where he does use a loop
Gerrie Schenck
+3  A: 

Right, now you've modified the question, it makes complete sense. You're modifying a variable used within a closure - which can produce unexpected results:

var now = new DateTime(1970, 1, 1);
var dates = new List<DateTime>();
var query = dates.Where(d => d > now);
...
now = new DateTime(1990, 1, 1);
foreach (DateTime date in query)
{
    // This will only see dates after 1990, not after 1970
    // This would confuse many developers.
}

In fact, it's not just a matter of when the query starts - you could modify it while iterating over the results:

var now = new DateTime(1970, 1, 1);
var dates = new List<DateTime>();
var query = dates.Where(d => d > now);
...
foreach (DateTime date in query)
{
    now = date;
    Console.WriteLine(date);
}

That will give a strictly-increasing sequence of dates... again, somewhat confusing.

R# is absolutely right to warn about this, IMO. It can sometimes be useful - but should be used with great care.

Jon Skeet
What if I call `ToList()`, shouldn't this execute the query immediately, thus preventing this situation?
hmemcpy
@hmemcpy: Yes, that's right.
Jon Skeet
But the warning remains, so maybe this might be a ReSharper bug still...
hmemcpy
@hmemcpy: The warning remains because R# doesn't know that the closure is never used again. Is it mean to know about every method which might use a closure? R# doesn't know that `ToList()` will execute the query immediately and then throw away the source.
Jon Skeet
+1  A: 

What ReSharper is warning you about is that the value of now is being captured in the lambda expression and is not the value you think it is when the lambda gets executed.

To solve your problem, you need to assign the value of now to a local variable before you use it:

var now = new DateTime(1970, 1, 1);
var dates = new List<DateTime>{new DateTime(2001, 12, 12)};
DateTime localNow = now;
dates.Where(d => d > localNow);

now = new DateTime(2003, 12, 12);

If you want to read more, the ReSharper forum has a post on it which includes several links with further explanation.

adrianbanks