简体   繁体   中英

Why do lambdas require => as part of their syntax?

I came across this line of code in a book:

var data = db.Query(sql).OrderByDescending(c => c.DatePosted); 

This sort of thing seems like tail-chasing to me ...

Why is the c => c. necessary? Wouldn't the following be understandable (by the compiler, and even more so by humans)?

var data = db.Query(sql).OrderByDescending(DatePosted); 

I'm sure there's all kinds of fancy stuff going on behind the scenes that purportedly needs this weird lambda syntax, but again:

Why? Is it really necessary? Can't the compiler figure out DatePosted is the column by which to sort?

...why is the c => c. necessary? Wouldn't the following be understandable (by the compiler, and even more so by humans...

First off, if you prefer your queries to be readable by humans and elide the lambda then write your queries using the syntax that is readable by humans and elides the lambda:

query = from record in records 
        orderby record.DatePosted descending
        select record;

I prefer that syntax for the reasons you state: it's easier to read and it emphasizes the semantics of the query in the business domain rather than the fact that the ordering mechanism requires a key selection function.

Second, the c=>c. is not necessary. Suppose you have a method:

static DateTime DatePosted(Record record) { return record.DatePosted; }

Now it is perfectly legal to use the syntax you want:

records.OrderByDescending(DatePosted)

The compiler will deduce that DatePosted is the function to call to obtain the sort key and construct a delegate appropriately. (Note that it will also deduce the types correctly and work out that you meant OrderByDescending<Record, DateTime> .)

More generally though: a basic design principle of C# is that the language does not guess what you meant except in certain very well-defined situations(*). A query can be ordered by literally anything , and it is therefore beholden upon the author of the code to clearly state what the sort key is. In the query syntax, the range variables that represent elements of the collection are available so that you can easily expression "order by the date associated with each record". To just say "order by the date" would then involve making a guess.

For the lambda syntax, the method requires a function that selects the order key. You can supply the required a function by supplying a value of delegate type, a lambda convertible to a value of delegate type, or a method group convertible to a value of delegate type, as you see fit. A "naked" property is not a function.

So, could the C# language have been designed so that when you provide the name of a property without any context explaining what its a property of, that the compiler makes a guess? That it says "oh, I see there's a method called OrderBy here that takes a function and a sequence, let me make a guess that the name supplied for the function is intended to be the function "fetch this property off of a given element of the sequence"? Sure, I could have done that. The code is not that much harder to write. But that would not be in keeping with the design principles of the C# language. The benefit of saving a couple keystrokes is not worth the pain of violating the design principle that the intent is unambiguously stated in the code .


(*) Like implicitly typed locals, method type inference, implicitly typed arrays, and overload resolution, all of which involve the compiler making a best guess as to what the developer intended. The rules for making those guesses are carefully documented and have been designed so that most will bail out early if any ambiguity is detected.

Just writing DatePosted by itself might refer to a type, field, property or method group. What if it referred to a method group that, by chance, took the right type of argument? Then this would do the wrong thing.

Being explicit, sometimes, is not a bad thing. The syntax

c => c.DatePosted

means specifically a function that takes an input and returns the DatePosted field/property of that input. This is flexible -- you can write

c => DatePosted(c)

or

c => c.DatePosted()

or

c => new DatePosted(c)

depending on what you need to do.

Some educated guessing here, but...

Mainly, because "DatePosted" has no intrinsic meaning by itself. It could be a method, a property, a type, anything. It also doesn't match the method signature of Func<TSource, TKey> , but I'm assuming you mean "if the compiler were smarter".

You can , however, pass in something that matches the signature, like so:

public class Foo
{
    public int Age {get; set;}
}

public int GetFooAge(Foo f) { return f.Foo; }

var source = <some mess'o Foos>;

source.OrderByDescending(GetFooAge).Dump();

I think the key point is that such a feature would introduce a disturbance to the context. What is "DatePosted" and where is it defined? The compiler treats method calls in a certain way and introducing a special kind of method call would be tricky. How do we know this should be a special method call? It is simply more pragmatic to give the compiler all the context it needs. That's why the succinct lambda syntax is so helpful.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM