A while a go, the C# community got surprised by another GOTCHA, discovering that using a foreach iteration variable inside a LINQ query, may not always yield the expected results. The variable gets captured by the foreach loop and, due to the evil work of closure, it remains scoped outside of the LINQ query itself living its own life so to speak. This has now been corrected in the Visual Studio 11 Beta.
Consider a simple example. Let’s start with a very basic Team class. Here it is.
public class Team
{
public string name { get; set; }
public Team(string n) { name = n;}
}
Ok, now let’s take a generic list of our Team objects. We’ll also need an Enumerable to iterate through (in our case 4 iterations is enough ,as it corresponds to the List size), and a container for IEnumerable results of LINQ query we are gonna execute.
List teams = new List();
teams.AddRange(new Team[] {
new Team("Maple Leafs"), new Team("Canadiens"), new Team("Oilers"), new Team("Senators")
});
var count = Enumerable.Range(0, 4);
List> teamsEnum = new List>();
Alright, so far so good. Let’s loop through the Enumerable and use the iteration variable to select the Team from the collection and add it to the list container. Easy-peasy.
foreach (var index in count)
{
var t = from team in teams
where team.name == teams[index].name
select team.name;
teamsEnum.Add(t);
}
Now all we gotta do is just loop through the result list, and in there loop through IEnumerable and display the results.
foreach (var t in teamsEnum)
{
foreach (var item in t)
Console.WriteLine(item);
Console.WriteLine();
}
Console.ReadLine();
This is definitely not rocket since, so it’s easy to predict the output.
- Maple Leafs
- Canadiens
- Oilers
- Senators
Right? Gotcha. Visual Studio 2010 produces this.
This problem lies in the foreach loop, which captured the iteration variable and kept changing it upon every iteration, thus affecting whatever we stored in the List<IEnumerable
Thankfully, this has been corrected in Visual Studio 11 Beta. Executing the same code in VS11b produces the following output, as expected.
So, if you have some old code that has been misbehaving, or you needed to copy variables inside the loop to avoid it, all you need to do now to resolve it is to recompile your code using VS11b.