The analyzer has detected that a captured variable used in the LINQ method with deferred execution has been changed. In this case, the original variable value is not considered.
Take a look at an example:
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(); }
In the ProcessNames
method, names are filtered by the first letter. The expected result is that the names
variable will contain names that do not start with T
and B
. However, the program behaves differently. After executing this method, names
will have a collection with Tucker
and David
.
This behavior occurs because when Where
is called, the filtering is not executed immediately, but is deferred. Since B
is assigned to the startLetter
variable between the two calls to Where
, the T
value is not considered in the final evaluation.
This happens because startLetter
is captured during the first call to Where
, and its value has changed before the second call to Where
. As a result, the final collection will be filtered only by B
.
You can learn more about deferred execution here.
To ensure the method operates correctly, either immediately execute the first Where
query and work with its result or use a different variable in the lambda expression of the second Where
.
Let's look at the first scenario:
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(); }
After the first Where
method, ToList
is called, creating a collection filtered by the first Where
. By the time the captured variable changes, the collection has already been formed, so the filtering behaves correctly.
The second fix option:
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(); }
In this case, a new value between Where
calls is not assigned to the captured variable, the startLetter2
variable is used instead. As a result, the filtering will operate correctly.