C# LINQ Deferred Execution and Nested Methods

66 Views Asked by At

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?

1

There are 1 best solutions below

7
On BEST ANSWER

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.