V3219. The variable was changed after it was captured in a LINQ method with deferred execution. The original value will not be used when the method is executed.
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.