对LINQ参数感到困惑

5

我正在尝试理解LINQ并自信地使用它。 我所困扰的是所需的参数。 例如:

var sortedWords = words.OrderBy(a=>a.Length)

words 是一个数组集合。 OrderBy 的智能感知提示:

Func<string, TKey> keyselector

一个func执行一个方法,而string是值,TKey是键。
在例子http://msdn.microsoft.com/en-us/vcsharp/aa336756.aspx#thenBySimpleThenBy - 比较器),我们通过a => a.Length比较长度。我理解这个语法,但它与智能感知要求的有什么关系呢?
由于所有的泛型,我发现方法签名和智能感知难以阅读。
谢谢。
5个回答

6

如果您了解.NET/C#中lambda表达式的本质,那么Intellisense显示的类型就有意义了。否则,对于新手来说,它确实可能会显得有点奇怪。首先考虑keySelector的类型Func<TSource, TKey>只是一个委托。在C# 3.0之前,您需要通过将委托作为参数传递来调用此类方法,例如:

IEnumerable<string> sortedWords = words.OrderBy(new Func<string, int>(mySelectorMethod));

其中mySelectorMethod是一个普通方法的名称,它接受一个字符串作为参数并返回一个整数。(顺便说一下,我想你可以使用匿名委托,但现在先不要去那里。)此外,请注意,这个例子仅仅是说明性的,因为LINQ几乎总是与.NET 3.5/C# 3.0一起使用(尽管我相信它也可以与.NET 2.0/C# 2.0一起使用-如果我错了,有人请纠正我)。自C# 3.0以来,方法可以内联定义为lambda表达式,这些表达式旨在在正好这种情况下使用。如果您想要获得适当的介绍,请阅读上面链接的MSDN文章,但在这里,我将简单地描述在这个特定上下文中的使用。正如您所述,您的代码(C# 3.0)大致如下:

var sortedWords = words.OrderBy(a => a.Length);

表达式中的部分a => a.Length是lambda表达式,实际上只是声明内联函数的简写。Lambda表达式的语法大部分是相当简单的;在=>左侧指定参数,通常以(arg1, arg2, arg3)的形式,但在这种情况下只有一个参数,可以省略括号。=>右侧是函数(更准确地说是Lambda表达式)的返回值表达式。或者,您可以使用{和}将实际代码与return语句括起来,尽管这通常是不必要的。我认为C#编译器所做的是将传递给OrderBy的参数识别为lambda表达式,然后将其编译成函数并创建并传递委托。请注意,Lambda表达式也可以转换为System.Linq.Expressions.Expression对象(可访问的表达式树),而不是委托,但这是一个更少见的用法。总之,在幕后进行了很多工作,但希望这至少能说明为什么类型是Func以及它如何与Lambda表达式相关。正如我所说,如果您想更深入地了解LINQ / lambdas / delegates,请阅读MSDN...

5
a => a.Length

我理解这个语法,但它与 IntelliSense 要求的有什么关系呢?
这段代码是一个 lambda 表达式。Lambda 表达式是一种方便生成匿名方法(在本例中)或 System.Linq.Expressions.Expression 的方法。让我们逐个部分来看。
最引人注目的特征是 "=>",它将参数与方法体分开。
在 "=>" 的左侧,有一个符号: "a"。这是我们匿名方法的参数声明。编译器知道我们正在调用 OrderBy(),而 OrderBy 需要一个 Func。这样一个函数的参数是一个字符串,所以编译器确定 "a" 必须是一个字符串。程序员需要提供的唯一内容就是名称。
在 "=>" 的右侧,有一个方法体。由于这是一行代码,因此 "return" 关键字是暗示的。IDE 对 "a" 作为字符串提供 IntelliSense,使您可以使用 Length 属性。
现在,请考虑这个 C# 2.0 ...
IEnumerable<string> sortedWords = 
  Enumerable.OrderBy(words, delegate(string a) {return a.Length;});

使用C# 3.0

IEnumerable<string> sortedWords = words
  .OrderBy(a => a.Length);

谢谢你。现在完全明白了! :) - GurdeepS
另外,如果我想先获取最短的字符串,除了在集合上使用 a => a.Length 然后进行 Reverse 之外,还有其他方法吗? - GurdeepS
如果您想要更改排序顺序,请查看 OrderBy、OrderByDescending、ThenBy 和 ThenByDescending。 - Amy B

1

我认为 IntelliSense 实际上非常有用,特别是对于以 Func<..> 类型作为参数的泛型方法,因为您可以看到类型,并且类型指导您理解该方法可能执行的操作。

例如,OrderBy 的参数是 IEnumerable<string> 作为“this”参数,这意味着我们有包含字符串集合的某些输入。第一个参数 keySelector 的类型为 Func<string, TKey>,这意味着它是您提供的一些 lambda 表达式,指定如何从 string 中获取 TKey

这已经表明该方法可能会枚举集合中的所有项(字符串),并且它可以使用 keySelector 从集合中的每个元素获取类型为 TKey 的值。名称 TKey 已经表明它将使用此值来比较使用此计算键的元素(字符串)。但是,如果您查看另一个重载,它采用 IComparer<TKey>,那么您可以确定 - 此参数指定有关如何比较类型为 TKey 的两个值的更多详细信息,因此函数必须使用此键比较元素。

... 这种类型的思考需要一些时间来适应,但一旦学会了,它可以非常有帮助。它在“功能”代码风格中更有用,这种风格通常在 C# 3.0 中使用许多泛型和 lambda 表达式(以及类似的功能语言如 F# 或其他语言)。


为了让自己更清楚,实际上是智能感知句子的呈现和间距使其有点难看。 :) - GurdeepS
是的,我同意这可能需要更清晰一些。我认为使用C#语法可能有点困难。您可以尝试看看F#,它以更简洁的方式显示类似的信息... - Tomas Petricek

0

说实话,我从来不担心Intellisense。在我的Linqage早期,它曾经让我困扰过。随着我花更多的时间研究泛型和表达式,它开始变得有意义了,但在那之前,我只是把语法塞进去。

Linq想要的是一个lambda表达式,告诉它在排序集合时要查找什么。

我理解你,兄弟,坚持下去,很快就会明白的。


0

OrderBy() 接受一个单参数的函数代理(在您的情况下为 string),并返回替换 TKey 的类型值。可能已经确定了参数类型(字符串),因为您在 IEnumerable<string> 上调用了该方法,但是委托类型仅在完全指定 lambda 表达式(即 a => a.Length)时从中推断出 Func<string, int>。如果您还没有向解析器提供有关所需排序键的任何线索,则 IntelliSense 中将仅显示 TKey,直到可以确定预期类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接