我在一本书中遇到了这行代码:
var data = db.Query(sql).OrderByDescending(c => c.DatePosted);
这种事情似乎跟我追尾......
为什么是必需的c => c.
?以下是不可理解的(编译器,人类更是如此)?
var data = db.Query(sql).OrderByDescending(DatePosted);
我确信幕后发生了各种奇特的事情,据称需要这种奇怪的lambda语法,但是又一次:
为什么?真的有必要吗?编译器不能弄清楚DatePosted是要排序的列吗?
答案 0 :(得分: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#的设计原则保存几次击键的好处是不值得违反设计原则的痛苦,意图在代码中明确陈述。
(*)与隐式类型的本地化,方法类型推断,隐式类型化数组和重载决策一样,所有这些都涉及编译器对开发人员的意图做出最佳猜测。制定这些猜测的规则是经过仔细记录的,并且经过精心设计,如果发现任何歧义,大多数人都会提前纾困。
答案 1 :(得分:5)
单独编写DatePosted
可能会引用类型,字段,属性或方法组。如果它提到的方法组偶然采取了正确的参数类型怎么办?那么这会做错事。
有时,明确并不是一件坏事。语法
c => c.DatePosted
特指一个接受输入并返回该输入的DatePosted
字段/属性的函数。这很灵活 - 你可以写
c => DatePosted(c)
或
c => c.DatePosted()
或
c => new DatePosted(c)
取决于您需要做什么。
答案 2 :(得分: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();
答案 3 :(得分:2)
我认为关键点在于这样的功能会对上下文造成干扰。什么是“DatePosted”以及它在何处定义?编译器以某种方式处理方法调用,并引入一种特殊的方法调用将是棘手的。我们怎么知道这应该是一个特殊的方法调用?为编译器提供所需的所有上下文更为实用。这就是为什么简洁的lambda语法非常有用。