6

我在一本书中遇到了这行代码:

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

这种事情对我来说似乎是在追尾......

为什么c => c.必要的?以下内容不是可以理解的吗(编译器,甚至人类更是如此)?

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

我敢肯定,在幕后发生了各种奇特的事情,据称需要这种奇怪的 lambda 语法,但又一次:

为什么?真的有必要吗?编译器不能确定 DatePosted 是排序的列吗?

4

4 回答 4

27

...为什么是c => c.必要的?以下内容不是可以理解的(编译器,甚至人类更是如此......

首先,如果您希望您的查询被人类阅读并省略 lambda,那么使用人类可读的语法编写您的查询并省略 lambda:

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

由于您陈述的原因,我更喜欢这种语法:它更易于阅读,并且强调业务领域中查询的语义,而不是排序机制需要键选择功能这一事实。

c=>c.没有必要。假设你有一个方法:

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

现在使用你想要的语法是完全合法的:

records.OrderByDescending(DatePosted)

编译器将推断出DatePosted调用该函数以获取排序键并适当地构造委托。(请注意,它还将正确推断类型并计算出您的意思OrderByDescending<Record, DateTime>。)

更一般地说:C# 的基本设计原则是该语言不会猜测您的意思,除非在某些非常明确的情况下(*)。查询可以按字面意思进行排序,因此代码作者必须清楚地说明排序键是什么在查询语法中,表示集合元素的范围变量可用,以便您可以轻松地表达“按与每条记录关联的日期排序”。仅仅说“按日期订购”将涉及猜测。

对于 lambda 语法,该方法需要一个选择顺序键的函数。您可以通过提供委托类型的值、可转换为委托类型的值的 lambda 或可转换为委托类型的值的方法组来提供所需的函数,只要您认为合适。“裸”属性不是函数。

那么, C# 语言是否可以被设计成当你提供一个属性的名称而没有任何上下文解释它的属性时,编译器会做出猜测?它说“哦,我看到这里有一个名为 OrderBy 的方法,它接受一个函数和一个序列,让我猜测为函数提供的名称旨在成为函数”从给定元素中获取此属性序列”? 当然,我可以这样做。代码编写起来并没有那么难。但这不符合 C# 语言的设计原则。节省几次击键的好处不值得痛苦违反了代码中明确说明意图的设计原则。


(*) 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.

于 2013-03-07T04:34:55.937 回答
5

单独编写DatePosted可能会引用类型、字段、属性或方法组。如果它引用的方法组碰巧采用了正确类型的参数怎么办?那么这会做错事。

有时,直言不讳并不是一件坏事。语法

c => c.DatePosted

专门表示接受输入并返回该输入的DatePosted字段/属性的函数。这很灵活——你可以写

c => DatePosted(c)

或者

c => c.DatePosted()

或者

c => new DatePosted(c)

取决于你需要做什么。

于 2013-03-07T03:28:46.647 回答
4

这里有一些有根据的猜测,但是...

主要是因为“DatePosted”本身没有内在意义。它可以是一个方法、一个属性、一个类型,或者任何东西。它也与 的方法签名不匹配Func<TSource, TKey>,但我假设您的意思是“如果编译器更聪明”。

但是,您可以传入与签名匹配的内容,如下所示:

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();
于 2013-03-07T03:29:43.223 回答
2

我认为关键是这样的功能会给上下文带来干扰。什么是“DatePosted”,它在哪里定义?编译器以某种方式处理方法调用,引入一种特殊的方法调用会很棘手。我们怎么知道这应该是一个特殊的方法调用?为编译器提供所需的所有上下文更加实用。这就是简洁的 lambda 语法如此有用的原因。

于 2013-03-07T03:27:35.027 回答