roslyn: Proposal: return, break and continue expressions

Since throw is now an expression, I think return , break and continue are also good candidates to be an expression. Obviously, return shouldn’t be legal in expression bodied methods.

var c = obj as C ?? return;
// instead of
if(!(obj is C c)) return;
let C c = obj else return;

var o = F() ?? return;
// instead of
var o = F();
if (o == null) return;

foreach(var item in list) {
  var result = item match {
    case 1: "one",
    case 2: "two",
    case 3: break,
    default: continue,
  };
}

There is no straightforward alternative for the third example as you will need to use the switch statement.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 11
  • Comments: 29 (9 by maintainers)

Most upvoted comments

@alrz

So, currently there is no way to break out of a loop in switch’s case body.

Yes there is.

foreach(var a in b)
{
  switch(a)
  {
    default: goto done; 
  }
}
done: 

@DavidArno Discussing the topic is fine. Attempting to derail with comments like:

Sadly though, @MadsTorgersen (and especially @CyrusNajmabadi) seem hell-bent on ignoring sage advice on the topic so I doubt this proposal will happen (and variable scope leakage will happen).

is not.

Neither is:

Then stormed up in a huff and said "La, la; la, la. I’m not listening. I’ve made my decision. Like it or lump it

Please, let’s all be respectful, courteous, and professional here. Thanks!

And no, I absolutely will not contact you via email. Microsoft make great claims that C# is now “developed in the open”. That means we discuss language design decisions in the open. Not at MVC conferences; not via email; but right here on GitHub.

We also discuss things over email, and any of the many mediums that allow one to converse without muddying up another topic. This is the last i’m going to say about this. Please keep the discussion on the issue at hand. That means actually discussing the language feature and not your feelings about how your feedback is being treated. If you want to discuss that elsewhere, i welcome that. It’s just not appropriate here.

The Kotlin programming language has this feature and it turned out to be very valuable, especially in regards to null safety. Kotlin’s ?: operator is equivalent to C#'s ?? and the following code is very idiomatic:

val firstItem = list.firstOrNull() ?: return

for (e in listOfNullableValues) {
    val nonNullableE = e ?: continue
}

@MgSam,

You need to turn your “Now try and tell me how that goto there is harming your code in some way” on it’s head, to make it: “If there were no goto, how would I solve this?” Two possible answers:

  1. Replace it with return, ie take those loops and put them in their own function. Which is OK, but isn’t really that great a solution, or
  2. Get rid of the nested foreach’s, eg by replacing them with a SelectMany...Any linq expression, with no loops and no nesting. The code will more often than not end up being better structured and more readable.

That’s my point with “fearing” goto: it’s presence likely indicates the code around it isn’t “as nice as it could be”.

You only dislike it because you’ve been reading dogma your whole life about how “bad” goto is

Nope, it’s because I’ve spend decades working with crap code (lots of it my own from the past) and goto is a strong indication that I’m going near some pretty horrible code.

@DavidArno

The prototypical example in C# is breaking out of nested loops.

foreach(var a in b)
{
    foreach(var c in a)
    {
        if(c == foo) goto exit;
    }
}
exit:

Now try and tell me how that goto there is harming your code in some way. You only dislike it because you’ve been reading dogma your whole life about how “bad” goto is. The “alternatives” people suggest of factoring out the inner loop to a separate method or using a marker variable for two breaks are both harder to read and maintain.

goto in C# is included specifically for this purpose and for use in switch statements. It cannot jump between methods, and thus can’t do most of the horrible things you’ve heard about in tales from C.

In addition, I suspect you already use goto in your code- you just spell it break, continue, or return.

I digress, this is going off-topic from what this thread is about.

Please keep the discussion on topic. If you want to talk more about your concerns here, you are welcome to reach me at cyrusn@microsoft.com.

@CyrusNajmabadi

I’m sorry we haven’t been able to produce a solution you like 😦

Really? That seems odd. @MadsTorgersen turned up and said “right, listen up, this is what we are doing”, then later on rocked up and said “Scrap that, we are doing this instead”. Then stormed up in a huff and said “La, la; la, la. I’m not listening. I’ve made my decision. Like it or lump it”. So at what point did you feel sorry that this amazingly interactive process failed to result in a solution that people liked?

@Gentlehag,

Oh and for what it’s worth, I 100% agree with you with regards to local functions too. We asked for implicitly typed lambdas and instead got given the nested method groups code smell. 😖

@Gentlehag,

Since witnessing the train wreck that the design team are making to variable scoping to satisfy their desire to be able to write:

if (!TryX(out var result)) return;
// use result here

I have become a big fan of @alrz’s return, break and continue expressions proposal. The above could then be written as:

var result = TryX(out var x) ? x : return;
// use result here

The latter achieves the same result without causing scope leakage.

I was originally sceptical of this idea, but the irresponsible way that the language is being broken to make out var’s more useful, coupled with good examples like that from @cypressious, have convinced me that this is a great proposal.

Sadly though, @MadsTorgersen (and especially @CyrusNajmabadi) seem hell-bent on ignoring sage advice on the topic so I doubt this proposal will happen (and variable scope leakage will happen). 😞