解释Linq Microsoft Select - 索引[示例]

时间:2011-06-01 16:08:42

标签: c# linq

我正在运行微软的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)很可能是我缺乏理解的关键。

enter image description here

6 个答案:

答案 0 :(得分:6)

我不确定你在问什么,但是Select在从索引0开始的列表上进行迭代。如果当前索引处的元素值等于索引,它将设置{匿名对象中的{1}}属性为true。我的猜测是上面的代码在3,6和7中打印为true,对吗?

如果你写下你不理解的内容,也会更容易解释。

Jon Skeet写了一系列博客文章,他实施了linq,在这里阅读了InPlaceReimplementation 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开始。