1

I am getting some behavior from a LINQ Where clause that I can't quite grok.

For instance, I am trying to search a list for terms using a AND semantic.

Here is some code:

var items = new List<string>()
{
    "beef",
    "beef soup",
    "beef ribs",
    "chicken soup",
    "soup",
    "pork ribs",
    "ribs"
};

var term = "soup beef";
var terms = term.Split(' ');
var result = items.AsQueryable();
foreach(var t in terms)
{
    result = result.Where( i => i.Contains(t) );
}
result.ToList().Dump();

The result are:

  • beef beef
  • soup
  • beef ribs

However, I was looking for it to an AND result, returning just:

  • beef soup

Now, I CAN GET THE RESULT I WANT by simply adding

.ToList().AsQueryable()

to the end of the Where clause.

Of course, this is not what I want to do if the back end is a database.

What is really odd here is that when I check 'result' in the loop, I get the following:

  1. Before first where clause => full list -> makes sense.
  2. After first where (for 'soup') => ['beef soup', 'chicken soup', 'soup'] -> still looks good
  3. Before second loop (for 'beef') => ['beef', 'beef soup', 'beef ribs'] -> WHOA! Whats up here
  4. After second loop => stays the same

Can someone explain to me what is going on (and how to fix this correctly)?

teleball
  • 4,602
  • 6
  • 37
  • 38
  • Your question is often asked, and you've already found it's due to closing over the loop variable. See: http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx Note that they're considering [changing the behavior in C# 5](http://stackoverflow.com/questions/7123898/lambda-capture-problem-with-iterators/7124236#7124236). – Anthony Pegram Aug 23 '11 at 15:41
  • Confirmed. They are making the breaking change in C#5. This makes `foreach` loops uniquely different from `for` loops. – Ishmael Smyrnow Jan 28 '12 at 00:17

2 Answers2

3

Review Access To Modified Closure

http://weblogs.asp.net/fbouma/archive/2009/06/25/linq-beware-of-the-access-to-modified-closure-demon.aspx

Essentially the problem is that t is being modified in your foreach loop, but you are using it within your closure (the Where lambda method), which is accessing t by reference.

Jeff
  • 35,755
  • 15
  • 108
  • 220
0

CLOSURES!!! Arg.... Got it.

    var cTerm = t;

    result = result.Where( i => i.Contains(cTerm) );

Sorry for the randomness...

teleball
  • 4,602
  • 6
  • 37
  • 38