1

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
  • 2
    Your initialisation of query captures the values of start and end at the time that it is initialised, so changing their values afterwards has no effect on the query. Commented Dec 14, 2023 at 11:48

1 Answer 1

0

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.

Sign up to request clarification or add additional context in comments.

7 Comments

Hello. Thank you for answering. Why does this happen? Why does GetRange receive a copy of the original values? Is there a way to make this work?
Is this because the GetRange method gets executed right away and stores its' results, and only where gets called by later calls?
@MichailGolubjev this happens because method parameters are passed by value (see the link in the answer and the usage of ref in the FirstMethod). "Is there a way to make this work?" - seems to be a bit of XY problem. Why exactly do you want that?
Well, I've written some code that relies on my assumption, now I will have to rewrite it. Also, I want to understand the correct mechanism. Why does LINQ work with updated variables when using deferred execution, but this code doesn't?
@Guru Stron Instead of 2 where... u can just do ints.Where((v, index) => index >= start && index <= end && v < limit);
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.