Reputation: 3747
I have this function for sorting by multiple user selected columns:
public override void Sort(SortFields sortFields)
{
if (sortFields == null || sortFields.Count() == 0) return;
var res = FilteredItems.OrderBy(o => o[sortFields[0].Name], sortFields[0].Ascending ? comparer : comparerDesc);
for (int x = 1; x < sortFields.Count(); x++)
res = res.ThenBy(o => o[sortFields[x].Name], sortFields[x].Ascending ? comparer : comparerDesc);
FilteredItems = new BindingList<T>(res.ToList());
if (ListChanged != null)
ListChanged(this, new ListChangedEventArgs(ListChangedType.Reset, null));
}
The problem, of course, is that when res.ToList() is called x has been incremented to one greater than the highest index in the list and it throws an error. I know I could go ahead and do a ToList() after each sort but that defeats the purpose and isn't even guaranteed to sort correctly with all linq providers. I'm sure there's a simple solution to this... anyone want to tell me what it is? :)
Upvotes: 1
Views: 85
Reputation: 66439
It looks like you may be getting tripped up by a closure over the variable x
.
That single variable is incremented on every iteration of the loop. It's incremented one last time, at which point it's 1 greater than the number of items in "sortFields", your conditional statement evaluates to False
, and your for
loop ends.
As user2864740 pointed out, and Eric states in his article:
Closures close over variables, not over values.
So when you then call ToList()
, your chained ThenBy
statements retain the final value of the variable x
, which was 1 greater than the highest index.
Instead, assign the incrementer to an intermediary variable inside the loop. When you call ToList()
, the final value of x
won't matter.
for (int x = 1; x < sortFields.Count(); x++)
{
int y = x;
res = res.ThenBy(o => o[sortFields[y].Name], sortFields[y].Ascending ? comparer : comparerDesc);
}
Also from Eric's article, this is (hopefully) being corrected soon, though only in foreach
loops apparently, not for
loops:
In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The "for" loop will not be changed.
Upvotes: 4
Reputation: 3875
Try
var res = FilteredItems.OrderBy(o => o[sortFields[0].Name], sortFields[0].Ascending ? comparer : comparerDesc).ToList();
Upvotes: 0