我正在运行微软的101 LINQ Samples,我对这个查询如何知道如何将正确的int
值分配给正确的int
字段感到困惑:
public void Linq12()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var numsInPlace = numbers.Select((num, index) => new { Num = num, InPlace = (num == index) });
Console.WriteLine("Number: In-place?");
foreach (var n in numsInPlace)
{
Console.WriteLine("{0}: {1}", n.Num, n.InPlace);
}
}
我在SO #336758中看到以前的例子中有错误,但我更有可能错过了一些东西。
有人可以解释一下这个以及编译器如何正确理解这些数据吗?
修改
好的,我认为我的混乱来自LINQ扩展,它使 Select 功能正常工作。 Func 和两个 int 参数IEnumerable<TResult> IEnumerable<int>.Select(Func<int,int,TResult> selector)
很可能是我缺乏理解的关键。
答案 0 :(得分:6)
我不确定你在问什么,但是Select
在从索引0开始的列表上进行迭代。如果当前索引处的元素值等于索引,它将设置{匿名对象中的{1}}属性为true。我的猜测是上面的代码在3,6和7中打印为true,对吗?
如果你写下你不理解的内容,也会更容易解释。
Jon Skeet写了一系列博客文章,他实施了linq,在这里阅读了InPlace
:Reimplementation of Select
更新:我注意到你对其中一条评论的评论之一,似乎是lambda而不是linq本身让你感到困惑。如果您阅读Skeet的博客文章,您会发现Select
有两个重载:
Select
索引的public static IEnumerable<TResult> Select<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
public static IEnumerable<TResult> Select<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, int, TResult> selector)
与第二个重载匹配。如您所见,它是Select
的扩展名,在您的情况下是IEnumerable<TSource>
的列表,因此您在ints
上调用Select
并签名{{1}} {1}}成为:IEnumerable<int>
。如您所见,我对Select
更改了Select<int, TResult>(this IEnumerable<int> source, Func<int, int, TResult> selector)
,因为这是您TSource
的通用类型。我仍然使用int
,因为您使用的是匿名类型。这可能解释了一些部分?
答案 1 :(得分:2)
对我来说是正确的。
首先,您创建了一个匿名类型,并创建了Num和InPlace。然后 LINQ Select 只是迭代元素和元素的索引。如果你在没有linq和匿名类的情况下重写它,它将如下所示:
class NumsInPlace
{
public int Num { get; set; }
public bool InPlace { get; set; }
}
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
List<NumsInPlace> numsInPlace = new List<int>();
for (int index = 0; i < numbers.length; i++)
{
int num = numers[index];
numsInPlace.Add(new NumsInPlace() { Num = num, InPlace = (index == num) });
}
Console.WriteLine("Number: In-place?");
foreach (var n in numsInPlace)
{
Console.WriteLine("{0}: {1}", n.Num, n.InPlace);
}
MSDN on Enumerable.Select具有详细信息,但投影函数(num,index)始终具有项目优先,然后是索引第二(如果提供)。
答案 2 :(得分:2)
lambda表达式(num, index) => new { Num = num, InPlace = (num == index) }
对输入序列中的每个元素执行一次,并传递该项并将其索引作为参数。
<强>更新强>
Lambda expressions可以隐式输入,也就是说,从所需类型开始,编译器可以找出(或暗示)你想要参数的类型。
(num, index) => new { Num = num, InPlace = (num == index) }
相当于
someAnonymousType MyMethod(int num, int index)
{
return new
{
Num = num,
InPlace = (num == index)
};
}
显然你不能写后者,因为你不能输入匿名类型的名称,但编译器可以;)
编译器知道这一点,因为the overload of Select
that you're using接受Func<TSource, Int32, TResult>
,这是一个Func
,它带有两个TSource
类型的参数(IEnumberable<T>
的类型在这种情况下,int
)和一个Int32(代表索引)并返回一个TResult
对象,它是你从函数返回的任何东西,在这种情况下,是一个匿名类型。 / p>
可以将lambda强制转换为所需类型,因此只能正常工作。
答案 3 :(得分:2)
编译器如何知道LINQ Select使用索引值作为数组的索引?
编译器不知道索引值。 Select的重载的实现知道索引值。
//all the compiler sees is a method that accepts 2 int parameters and returns a bool.
Func<int, int, bool> twoIntFunc = (x, y) => (x == y);
//The compiler sees there's an overload of Enumerable.Select which accepts such a method.
IEnumerable<bool> query = numbers.Select(twoIntFunc);
Enumerable.Select的implementation通过使用适当的参数调用该方法完成剩余的工作。
selector的第一个参数表示要处理的元素。 selector的第二个参数表示源序列中该元素的从零开始的索引。
所以 - 选择将首先用(5,0)调用你的方法,然后选择用(4,1)调用它。
答案 4 :(得分:1)
select中的第二个参数是索引,它在编译器遍历数字数组时递增。编译器将看到
num index num = index
5 0 false
4 1 false
1 2 false
3 3 true
9 4 false
8 5 false
6 6 true
7 7 true
2 8 false
0 9 false
答案 5 :(得分:0)
它为3,6和7提供了真实。你需要记住它从索引为0开始。