I have written the following code:
List<int> ints = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
int start = 0;
int end = 5;
int limit = 10;
IEnumerable<int> GetRange(List<int> list, int startGR, int endGR)
{
for (int i = startGR; i < endGR; i++)
{
yield return list[i];
}
}
IEnumerable<int> query = GetRange(ints, start, end).Where(x => x < limit);
void FirstMethod(IEnumerable<int> query1, ref int start1, ref int end1, ref int limit1)
{
start1 = 4;
end1 = 9;
limit1 = 8;
SecondMethod(query1);
start1 = 9;
end1 = 14;
limit1 = 16;
SecondMethod(query1);
}
void SecondMethod(IEnumerable<int> query2)
{
Console.WriteLine(string.Join(", ", query2));
}
FirstMethod(query, ref start, ref end, ref limit);
The console prints:
1, 2, 3, 4, 5
1, 2, 3, 4, 5
Instead of expected:
5, 6, 7
10, 11, 12, 13, 14
Please explain why doesn't deferred execution work here with updated values.
Is there a way to make this work as I expect?
GetRange(List<int> list, int startGR, int endGR)
will receive copy of passed start
and end
upon the invocation (i.e. "by value"), hence here:
IEnumerable<int> query = GetRange(ints, start, end).Where(x => x < limit);
after that ref
manipulations done with start
and end
in FirstMethod
will have no effect, so you will have a IEnumerable<int>
which will process range from 0
to 5
i.e. the initial values of the variables.
You can try using LINQ with it's deferred execution and the fact that lambda will capture the local variables via closures (as you do with Where(x => x < limit)
):
IEnumerable<int> query = ints
.Where((_, index) => index >= start && index <= end)
.Where(x => x < limit);
Though personally I'm not a big fun of such approach.