-1

Looking at expression bodied methods, I am trying to understand why the return keyword is not used.

For example with

public int GetAge() { return this.Age; }

I would have expected

public int GetAge() => return this.Age;

as opposed to the correct

public int GetAge() => this.Age;

Why is the return keyword not required/allowed in this case?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Tim
  • 115
  • 7
  • 2
    Why do you want to include the `return` keyword? There's nothing else that could be there, just the expression to return, so it's redundant. – madreflection Feb 11 '21 at 21:31
  • [Expression lambda that has an expression as its body](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions). And `this.Age` is an expression. – Riwen Feb 11 '21 at 21:31
  • 3
    Because this is the way this feature was designed =) And without this keyword code is much cleaner. – Guru Stron Feb 11 '21 at 21:32
  • 1
    Ignoring `void` for a moment - by definition it `... consists of a single expression that returns a value whose type matches the method's return type ...` And `return` as `The return statement terminates execution of the method in which it appears and returns control to the calling method` Therefore the `return` keyword is not used or required as the expression itself satisfies the function of the `return` statement. See https://stackoverflow.com/a/19224/426894 for differences between expression and statements. – asawyer Feb 11 '21 at 21:32
  • 1
    It's there, they just spelled it like `=>` instead to help people who [golf in C#](https://codegolf.stackexchange.com/a/121179/68810) .. Of course, it's never gonna beat something like [05AB1E](https://codegolf.stackexchange.com/a/121156/68810) which takes "spell this operation differently" to another level ;) – Caius Jard Feb 11 '21 at 21:38
  • 2
    You haven't explained why you expect a `return` to be in a context where an expression is expected. You just say you expect it without saying why, so it is hard to answer your "why not" question. What compelling user-focussed benefit do you see as justifying why a `return` should be required? If we can understand what language design principles you have in your head, that gives a good starting point for an answer to the question. – Eric Lippert Feb 11 '21 at 21:59
  • Because it's an **expression**. Expressions are just that: they express some operation or sequence of operations that inherently can be resolved to a single value. There's no sequence of statements, so no need to indicate to the compiler at what point the expression is _done_ and what value should be _returned_, which is the purpose of a `return` statement. Given that there's no need for either of that, i.e. no need for a `return` statement, why _should_ a `return` statement be required? It's as if you asked why when I declare a class that has only methods, I'm not required to declare ... – Peter Duniho Feb 11 '21 at 22:37
  • ... any properties too. Why should the language require you to provide something that's completely superfluous? If you can reword the question so that there's anything more to it than that, e.g. to address the concerns of the previous comment, I'm sure Eric will reopen (which he can do single-handedly) and answer with a detailed answer addressing whatever concern you actually have. – Peter Duniho Feb 11 '21 at 22:37
  • 1
    @PeterDuniho is signing me up for a service level that I'm not guaranteeing :) -- but his point is well taken. The question as it is stated right now is hard to answer. Most "why not" questions are hard to answer because they are often really about the belief system of the person asking the question, and that's not something we have access to. – Eric Lippert Feb 12 '21 at 19:06

1 Answers1

1

Expressions are short methods by convention. It's all about the delegation of a "thing" to do and what it takes to declare the contract: What you have and what you want in return.

It's pretty good to see in Lambdas: While you'll see this most of the time ...

    var tims = users.Where(u => u.Name.StartsWith("Tim"));

... it is perfectly valid to write ...

    var tims = users.Where((User u) => u.Name.StartsWith("Tim"));

... and even:

    var tims = users.Where((User u) => { return u.Name.StartsWith("Tim"); });

The latter is a full featured delegate syntax with arguments (User u) the curly braces for the method scope and the single-line body. In fact, this is quite the same method as this (except the lack of a method name):

    private bool IsTim(User u)
    {
        return u.Name.StartsWith("Tim");
    }

Which by the way would allow your lambda to look like this because IsTim() has exactly the same signature as the predicate for the Where-clause (= the lambda determining whether a user is a match or not).

    var tims = users.Where(IsTim);

Skipping the last example, all of the other three lambda predicates do exactly the same thing but look pretty different:

 1    u => u.Name.StartsWith("Tim")
 2    (User u) => u.Name.StartsWith("Tim")
 3    (User u) => { return u.Name.StartsWith("Tim"); }

Coming from case 3 to case 1 over the last years, the .NET team tried to minimize the keystrokes necessary to achieve the same thing. Delegates 3 have been there forever but with the introduction of Lambdas and Linq, Microsoft needed to simplify the delegating signature which led us to 2 and 1.

While case 2 is basically exact the same thing as case 3, case 1 is only valid in the correct context. It does not define the type of u but infers it in the context it is called. If you call this lambda on a list of users, u will be a user object. If you call it on a list of strings, it's representing a string (and won't compile in this case).

It took a while that Microsoft took over this logic to method and even property bodies but the logic is still the same: Why won't you reduce the necessary keystrokes if you can infer the arguments, return types and methods scopes by it's context?

That's why these two methods are exactly the same:

    private bool IsTim(User u)
    {
        return u.Name.StartsWith("Tim");
    }

    private bool IsTim(User u) => u.Name.StartsWith("Tim");

u.Name.StartsWith("Tim") will always return a boolean value and the runtime already knows the arrow => to split the method arguments from the method body. It really is "pointing" to the expression producing the return value.

"Pointing to a return value" explains why there should not be a return keyword in public int GetAge() => this.Age;.

Waescher
  • 5,361
  • 3
  • 34
  • 51