C# Loops – While Loop Without Evaluating Data Twice

cloops

I often come across the following pattern.

while(GetValue(i) != null)
{
    DoSomethingWith(GetValue(i++));
}

Here GetValue is executed twice. It would be much nicer to use a pattern where we can evaluate AND store the outcome of GetValue. In some case (streams) it is impossible to get the value twice (see alternatives below for workarounds). Is there any pattern or loop construct we can use?

Some alternatives that I thought of with their own drawbacks.

Alternative 1

// Variable outside of loop scope, extra if
var value = null;
do
{
    value = GetValue(i++);
    if(value != null) { DoSomethingWith(value); }
} while(value != null);

Alternative 2

// Two get values, variable outside of loop
var value = GetValue(i);
while(value != null)
{
  DoSometingWith(value);
  value = GetValue(++i);
}

Alternative 3

// Out only works on reference types, most enumerables do not have
// a TryGet like method so we need to create our own wrapper
Object value;
while(TryGetValue(i++, out value))
{
    DoSomethingWith(value);
}

Ideal world scenario (not valid C#)

while((var value = GetValue(i++)) != null)
{
    DoSomethingWith(value);
}

Best Answer

Near "Ideal World" Solution

The following is valid C#

public static void Main()
{
    var i = 0;
    string value;
    while ((value = GetValue(i++)) != null)
    {
        DoSomethingWith(value);
    }
}

private static string GetValue(int input)
{
    if (input > 20)
    {
        return null;
    }
    return input.ToString();
}

private static void DoSomethingWith(string value)
{
    Console.WriteLine(value);
}

Compare with your "Ideal world scenario":

var i = 0;
while((var value = GetValue(i++)) != null)
{
    DoSomethingWith(value);
}

All I had to do was to pull value outside the loop. You seem willing to do this for all the presented alternatives. Thus, I don't think this is too much of a stretch.


"Infinite" While alternative

I have been playing around with the alternatives... here is another one:

var i = 0;
while (true)
{
    var value = GetValue(i++);
    if (value == null)
    {
        break;
    }
    DoSomethingWith(value);
}

In this case you can declare value inside the loop (and using var), you don't need to check for null twice, you don't need to call GetValue twice with the same input, and you don't have to create a wrapper with an out parameter.


Alternative using For

We may try to express the same thing using a for loop. Although, the naive approach does not work:

// I repeat, this does not work:
var i = 0;
for (string value = null; value != null; value = GetValue(i++))
{
    DoSomethingWith(value);
}

The problem with this version is that it starts with value being null, which meets the exit criterion, and thus you get no iterations.


As JimmyJames points out you can write it like this:

var i = 0;
for(string value; (value = GetValue(i++)) != null;)
{
    DoSomethingWith(value);
}

The only drawback I see is that you need to write the type (you can't use var).


Addendum: This is another variant suggested by Maliafo:

var i = 0;
for (var value = GetValue(i); value != null; value = GetValue(++i))
{
    DoSomethingWith(value);
}

While this version requires to write GetValue, it doens't call it twice with the same value.


Do you want to declare i in the scope too? Have a look at this:

for (var i = 0; ; i++)
{
    var value = GetValue(i);
    if (value == null)
    {
        break;
    }
    DoSomethingWith(value);
}

This is the same loop that we see in the "infinite" while solution I posted above. Yet, since I had no condition on the while... why not use change it to a for to increment i?


Addendum: Also see NetMage's answer for an interesting use of C# 7.0 features.