9

Is there a concise way to loop over true/false in C#?

I have ~20 lines of code in a unit test I'd rather not duplicate to toggle one boolean true/false.

I could break it off into a function and call it twice, but meh. This code feels more like I'm iterating over possible values than performing a distinct action with different parameters. Even if I had a function, I'd prefer the syntax of looping over the possible values rather than just calling it twice.

I could write a for loop like so...

bool toggle;
for (int i = 0; i < 2; i++)
{
    toggle = i == 1;
}

But that doesn't seem very clean.

I like this syntax:

for (bool b : { false, true }) { /* ... */ }

But it doesn't look like that will compile in C#.

Edit:

Following Jeroen's suggestion about local functions and Dmitry's answer, this is the route I went:

[TestMethod]
public void GetAndSetValue()
{
    foreach (bool toggle in new [] { false, true })
    {
        GetAndSetValue(toggle);
    }

    void GetAndSetValue(bool toggle)
    {
        // details not important
    }
}

Reasonable coders can debate whether the loop reads more easily than two function calls:

GetAndSetValue(false);
GetAndSetValue(true);

I like the loop better, so I'll roll with it until someone complains. Cheers!

Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
crenshaw-dev
  • 7,504
  • 3
  • 45
  • 81
  • 3
    `foreach (bool b in new bool[] { false, true }) {...}` – Dmitry Bychenko Jul 04 '18 at 14:29
  • 4
    I really don't get why you think extracting this code into it's own function is a bad idea. For me, creating the loop as per Dmitry's answer is more complicated and less readable. – DavidG Jul 04 '18 at 14:32
  • `new[] { true, false }.ToList().ForEach(b => Console.WriteLine(b));` – Stevo Jul 04 '18 at 14:33
  • @DavidG even if I extracted to a function, writing the function call twice feels less expressive (correct term?) than looping over possible values. – crenshaw-dev Jul 04 '18 at 14:33
  • 2
    To add to what David said, don't forget that recent versions of C# have local functions, essentially making extracting such code free. Calling a function twice would be much more intuitive to me than "looping over every boolean", which would cause me to do a double take, even if it happens to semantically be what you're doing, simply because it's not usually the sort of thing you loop over. It's like looping over every possible even prime. – Jeroen Mostert Jul 04 '18 at 14:34
  • 1
    Why? I completely disagree with that I'm afraid. `DoStuff(true)` and `DoStuff(false)` looks a lot nicer than a `for` loop. The cognitive load to parse out what the loop is doing is surely not worth the effort. – DavidG Jul 04 '18 at 14:35
  • @DavidG matter of opinion, I guess... I would read that as "do a thing, then do a different thing" vs. "do all the relevant things." But like I say, my brain may be unique in that way. – crenshaw-dev Jul 04 '18 at 14:36
  • 2
    `I like the loop better, so I'll roll with it until someone complains.` - I am complaining so stop it. :) – Rand Random Jul 04 '18 at 15:22
  • 1
    @RandRandom yeah but you're just like... some random someone. :-P – crenshaw-dev Jul 04 '18 at 15:25
  • I agree with the OP that the true and false call is an ugly solution. For one thing is impossible to see if it could have been called with other values also. It is much cleaner to define the possible input set, and then a foreach over that. preferably in a named local variable to ease readability on what we are looping over. Had it been an enum we could use .GetValues. Unfortunately that is not supported by bool. (the funktion MIGHT work on say a tri-state, that implicitly converts from bool like True/False/NA and you would never know that the third option was never tested) – Eske Rahn Jul 18 '23 at 14:00
  • (... or the bool? type, with null as the third possible input). – Eske Rahn Jul 18 '23 at 14:13

4 Answers4

20

Correct syntax will be foreach, not for:

foreach (bool b in new [] { false, true }) {
   /* ... */
}
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • 1
    I like it. Not quite as pretty as the C++ version. And I wish it somehow expressed perfectly explicitly that `false` once and `true` once are the only values this loop is _ever_ intended to iterate over. i.e. no one could at a glance think, "I could totally add ten more trues and falses to this list". But now I'm really just being pedantic. :-P – crenshaw-dev Jul 04 '18 at 14:39
  • For a moment you had an edit with a `for` loop... what was wrong with that implementation? – crenshaw-dev Jul 04 '18 at 14:40
  • @mac9416: I doubt if I should have posted such *unreadable* `for (bool b = false; !b; b = true) {/*....*/}` even for reference only. – Dmitry Bychenko Jul 04 '18 at 14:41
  • 1
    Yes, obviously that should have been `for (bool b = false; !b; b = !b)`... :-P – Jeroen Mostert Jul 04 '18 at 14:43
  • Oddly, I find it elegant. But probably in the same way I find a prop plane more elegant than a 737 - nerdiness. Agreed, `foreach` is more readable. Thanks! – crenshaw-dev Jul 04 '18 at 14:43
  • 3
    Elegant or not, it doesn't actually work -- it only produces `false`. There's no concise way to do this with a `for` loop. – Jeroen Mostert Jul 04 '18 at 14:45
  • 1
    `for (bool? b = false; b != null; b = (b.Value ? (bool?)null : true))` ... I know, it's horrific. Just couldn't help myself. – crenshaw-dev Jul 04 '18 at 14:50
4

While I think simply writing a parametrized function is definitely the correct approach, the closest to that C++11 syntax that you can get in C# would be:

foreach (bool value in new [] { false, true })
{
    // ...
}
Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
Kwinten
  • 301
  • 1
  • 6
1

I would probably just do it this way, either with a local function:

[TestMethod]
public void GetAndSetValue()
{
    GetAndSetValue(false);

    void GetAndSetValue(bool toggle)
    {
        // details not important

        if (!toggle)
            GetAndSetValue(true);
    }
}

Or "old" school with a private method.

[TestMethod]
public void GetAndSetValue()
{
    GetAndSetValue(false);
}

private void GetAndSetValue(bool toggle)
{
    // details not important

    if (!toggle)
        GetAndSetValue(true);
}
Rand Random
  • 7,300
  • 10
  • 40
  • 88
  • Interesting approach... too bad that parameter can't be readonly. Some mislead person might toggle `toggle` inside the local function and accidentally kill the second call. – crenshaw-dev Jul 04 '18 at 15:31
  • I guess that's just a reason to keep the local function small. – crenshaw-dev Jul 04 '18 at 15:31
0

A bit late to the party - but this is a different solution I came up with:

for ((bool b, int n) = (false, 0); n < 2; b = true, n++) {
   /* ... */
}

It works in C# 7.3, so it's compatible with .NET Framework.

sfkleach
  • 707
  • 6
  • 9