Анализатор обнаружил изменение захваченной переменной, которая использовалась в LINQ методе с отложенным выполнением. При таком сценарии изначальное значение переменной не будет учитываться.
Рассмотрим пример:
private static List<string> _names = new() { "Tucker", "Bob", "David" }; public static void ProcessNames() { string startLetter = "T"; var names = _names.Where(c => !c.StartsWith(startLetter)); startLetter = "B"; names = names.Where(c => !c.StartsWith(startLetter)) .ToList(); }
В методе ProcessNames
производится фильтрация имён по первой букве. Ожидается, что в результате переменная names
будет содержать имена, не начинающиеся на T
и B
. Однако поведение программы будет иным. При выполнении этого метода в names
будет коллекция, состоящая из Tucker
и David
.
Подобное поведение обусловлено тем, что при вызове Where
фильтрация выполняется не в момент вызова, а является отложенной. Так как между двумя вызовами Where
переменной startLetter
присваивается B
, значение T
не учитывается при итоговом вычислении.
Это происходит из-за того, что startLetter
захвачена при первом вызове Where
, и её значение изменилось перед вторым вызовом Where
. Как следствие, будет получена коллекция, которая фильтруется только по B
.
Подробнее об отложенном выполнении можно узнать здесь.
Чтобы метод работал корректно, нужно либо немедленно выполнить запрос (первый Where
) и работать с результатом выполнения, либо использовать другую переменную в лямбда-выражении второго Where
.
Рассмотрим первый сценарий:
private static List<string> _names = new() { "Tucker", "Bob", "David" }; public static void ProcessNames() { string startLetter = "T"; var names = _names.Where(c => !c.StartsWith(startLetter)) .ToList(); startLetter = "B"; names = names.Where(c => !c.StartsWith(startLetter)) .ToList(); }
После первого метода Where
вызывается ToList
, при выполнении которого создаётся коллекция, отфильтрованная с помощью первого Where
. На момент изменения захваченной переменной коллекция уже сформирована, следовательно, фильтрация будет выполняться корректно.
Рассмотрим второй вариант исправления:
private static List<string> _names = new() { "Tucker", "Bob", "David" }; public static void ProcessNames() { string startLetter1 = "T"; var names = _names.Where(c => !c.StartsWith(startLetter1)); string startLetter2 = "B"; names = names.Where(c => !c.StartsWith(startLetter2)) .ToList(); }
В данном случае захваченной переменной не присваивается новое значение между вызовами Where
, вместо этого используется переменная startLetter2
. В результате фильтрация отработает корректно.