Как исправить это лямбда-выражение?

1

У меня есть эта функция для сортировки по нескольким выбранным пользователем столбцам:

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));
}

Проблема, конечно, в том, что когда res.ToList() называется x, увеличивается на один, превышающий самый высокий индекс в списке, и он выдает ошибку. Я знаю, что могу пойти вперед и сделать ToList() после каждого вида, но это побеждает цель, и даже не гарантируется корректная сортировка со всеми поставщиками linq. Я уверен, что есть простое решение для этого... кто-нибудь хочет сказать мне, что это такое? :)

Теги:
linq
lambda

2 ответа

4
Лучший ответ

Похоже, что вы можете столкнуться с закрытием по переменной x.

Эта единственная переменная увеличивается на каждой итерации цикла. Он увеличился в последний раз, и в этот момент он на 1 больше числа элементов в "sortFields", ваш условный оператор оценивается как " False, а ваш цикл for заканчивается.

Как указал пользователь2864740, и Эрик утверждает в своей статье:

Закрытие закрывается над переменными, а не над значениями.

Поэтому, когда вы вызываете ToList(), ваши ThenBy выражения ThenBy сохраняют окончательное значение переменной x, которая на 1 больше, чем самый высокий индекс.

Вместо этого присвойте инкремент промежуточной переменной внутри цикла. Когда вы вызываете ToList(), конечное значение x не имеет значения.

for (int x = 1; x < sortFields.Count(); x++)
{
    int y = x;
    res = res.ThenBy(o => o[sortFields[y].Name], sortFields[y].Ascending ? comparer : comparerDesc);
}

Также из статьи Эрика это (надеюсь) будет исправлено в ближайшее время, хотя только в петлях foreach видимому, не for циклов:

В С# 5 переменная цикла foreach будет логически внутри цикла, и поэтому замыкания будут закрываться по новой копии переменной каждый раз. Цикл "для" не будет изменен.

  • 0
    На самом деле, я думаю, что мне «нужно» закрытие, чтобы сделать эту работу правильно… просто не совсем уверен, как это сделать на макушке, но я уверен, что скажу «да», как только увижу это. Не уверен насчет этого, но ваш код будет иметь ту же проблему, что и мой ... за исключением того, что вместо использования x в индексе, выходящем за пределы диапазона, он будет использовать y в верхней части диапазона для каждого вызова лямбда-выражения.
  • 1
    @BVernon Это закрытие в любом случае. Разница в том, что с этим опубликованным ответом переменная y отличается или «свежа» в каждом цикле (где переменная x одинакова), а замыкания связываются с переменными, а не со значениями.
Показать ещё 4 комментария
0

Пытаться

var res = FilteredItems.OrderBy(o => o[sortFields[0].Name], sortFields[0].Ascending? comparer: comparerDesc).ToList();

  • 0
    res - это IOrderedEnumerable, который имеет метод расширения ThenBy (). ToList () будет сортировать x раз, а не один раз, и не будет гарантировано, что он будет в правильном порядке ... он будет гарантированно отсортирован только по последнему полю.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню