6

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);
}
  • BTW in the ideal world scenario I think that you meant to write "while((var value = GetValue(i++)) != null)", Isn't that right? – Zalomon May 31 '17 at 12:52
  • @Zalomon you're absolutely right, fixed the typo. – Roy T. May 31 '17 at 13:09
  • Exactly why is the ideal world scenario not valid c#? Consider – candied_orange May 31 '17 at 13:23
  • 2
    You can get pretty darn close to the "ideal world scenario", all you need is to declare value outside the loop. – Theraot May 31 '17 at 13:24
  • 3
    Can't you do this in C#? for(var value; (value = GetValue(i++)) != null;) – JimmyJames May 31 '17 at 13:36
13

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.

  • I experimented a bit with a similar while syntax, but I thought it didn't work. I never thought of moving the variable declaration outside. I guess that's the closest you can come to my ideal world solution. I also never thought of using a for loop like that. Great answer! – Roy T. May 31 '17 at 14:04
  • Coming a bit late on this one, but isn't it possible to use a more idiomatic for (var value = GetValue(i) ; value != null ; value = GetValue(++i)) in C# ? – Maliafo Jun 9 '17 at 22:12
  • @Maliafo Yes, you are right, that works! I didn't even consider it because I was avoiding writing GetValue twice. I completely missed it. Addendum: I'll add that to the answer. – Theraot Jun 9 '17 at 22:20
5

In C# 7.0, we can just (ab)use is:

while (GetValue(i++) is var value && value != null)
    DoSomethingWith(value);

With another C# 7.0 feature, we can create an extension method:

public static T As<T>(this T val, out T newvar) => newvar = val;

And at the expense of an out, create a variable and test the value:

while (GetValue(i++).As(out var value) != null) {
    DoSomethingWith(value);

Or use a variation that emulates the ideal world scenario:

public static T Let<T>(out T newvar, T val) => newvar = val;

while (Let(out var value, GetValue(i++)) != null) {
    DoSomethingWith(value);
  • 1
    I didn't think of combining C# 7.0 features with Theraot's suggestion. Neat! – Roy T. Jun 9 '17 at 7:34
  • 1
    I am really hoping declaration expressions (plus something like the C comma operator) makes it in a version of C# soon. – NetMage Jun 9 '17 at 17:52
4

Use a higher order function

Take a leaf out of functional programming's play book: if you have a recurring pattern in the structure of your code, make a function that contains the parts that stay the same, and pass the parts that vary in as function parameters (ie delegates, in C#). That way, it doesn't matter how ugly it is, you only have to wrote it once and hide it a way in a library somewhere.

(You'll have to forgive me dropping into Java here; my C# skillz are a bit rusty)

public static <T> void untilNull (Function<Integer,T> supplier, int i, Action<T>  action) {
    T v;
    while ((v = supplier.apply(i++)) != null)
        action.perform(v);
}

....
untilNull (this::getValue, 1, this::doSomethingWithValue);

Update - now with C# version:

public static <T> void UntilNull (Func<int,T> supplier, int i, Action<T> action) {
    T v;
    while ((v = supplier(i++)) != null) action(v);
}

....
UntilNull (this.GetValue, 1, this.DoSomethingWithValue);
  • I think the problem with functional programming patterns in languages like C# is that they are less efficient than the procedural code, sometimes much less efficient, because there is no inline expansion of the method. – NetMage Sep 28 '18 at 18:32
3

The Simple Solution

This solution is most certainly better than your alternatives 1 and 2 if you need minimal code to work out your problem

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

DRY Iterator Based Foreach compatible solution

In my opinion, for the best clean general solution, this iterator with function parameter based solution would work the best. No need to class specific wrappers and duplicate code provided by your third option and works for every problem like this (so I think its still better than the shorter inline while loop implementations). And better yet, if you are going for iterators anyway, why not put the nullcheck in the iterator itself? That way you could use this for just about any function that takes a position and can return null.

public static System.Collections.Generic.IEnumerable<T>  
    FunctionSequence(Func<int,T> f)  
{  
    int i = 0;
    while(true){
        value = f(i++);
        if(value != null) { 
            yield return value; 
        }
        else{
            break;
        }
    }
}  

foreach(GetValueType value in FunctionSequence<GetValueType>(GetValue)){
    DoSomethingWith(value);
}

Notice that now you need no boiler plate code for any function just use the function with the FunctionSequence iterator and you'll iterate to the first null element, you only needed to write it once, this


Further Generalizing the Iterator Based Solution

If you don't want to start at 0, you could always change the function signature to something like:

public static System.Collections.Generic.IEnumerable<T>  
    FunctionSequence(Func<int,T> f, int start = 0)  
{  
    int i = start;
    ...

And you could even change what your checking against, whether its null or an specific object returned!

public static System.Collections.Generic.IEnumerable<T>  
    FunctionSequence(Func<int,T> f, int start = 0, T terminator = null)  
{  
    int i = start;
    while(true){
        value = f(i++);
        if(value != terminator) { 
        ...

This I think is the best solution for your case.

0

I'd go with alternative 3, for I don't see a reason why out being available for reference types: foremost if you're not using a reference type you don't need to check for nulls; in case that you want some other validation such as "has this enum the proper value?" you just can use that check in the while and it would be executed only when it's fitting (or you could use ref instead of out, even though that means initializing the variable when declaring it)

  • The check on null is just an example here. It could also be something like > 0. Another problem with out is that you will have to write a wrapper for most standard enumerables. But I might be able to capture that in an extension method. – Roy T. May 31 '17 at 12:26
  • As I said in my answer the case that null is just an example you could check whatever you need inside TryGetValue. The way I see it this would be more expressive that your proposed solution that I think it would be just syntactic sugar: since it still needs check the return of the function, assign it to the variable and pass it to DoSomethingWith. – Zalomon May 31 '17 at 12:49
  • Doh, I didn't think of TryGetValue like that. And yes it would be syntactic sugar. But it would not lead to a variable declared in a higher scope than needed, and I would not need to repeat myself :). – Roy T. May 31 '17 at 13:11
0

How about an alternative 4:

Wrapping the GetValue into a IEnumerable/IEnumerator. You'll then be able to use the normal foreach syntax that allows for your ideal scenario. If you have dynamic content you don't need to implement a full blown Collection class, just a few functions around a yield statement will do the trick.

Should even enable you to use some Linq magic tricks with your data too.

0

I'm not doing C# but mostly Swift. In most cases I would write

for value in <some collection> { ... }

which is syntactic sugar for

while let value = iterator.next() { }

(The let value = expression evaluates expression, checks whether it is nil, if nil it exits, if not nil it stores a non-optional value into value. next () in an iterator returns an optional value; nil if the iterator is finished).

protected by gnat Jun 9 '17 at 5:44

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.