如何计算IEnumerable <t>的等级并将其存储在类型T </t>中

时间:2012-04-28 13:41:45

标签: c# closures captured-variable

我想计算IEnumerable列表中元素的等级并将其分配给成员。但是下面的代码仅在第一次调用时起作用。第二次通话从最后一个等级值开始。所以我没有输出012和012,而是得到012和345

    class MyClass
    {
        public string Name { get; set; }
        public int Rank { get; set; }
    }

    public void SecondTimeRankEvaluvate()
    {
        MyClass[] myArray = new MyClass[] 
        {
            new MyClass() { Name = "Foo" },
            new MyClass() { Name = "Bar" },
            new MyClass() { Name = "Baz" }
        };

        int r = 0;
        var first = myArray.Select(s => { s.Rank = r++; return s; });

        foreach (var item in first)
        {
            Console.Write(item.Rank);
        }
        // Prints 012
        Console.WriteLine("");
        foreach (var item in first)
        {
            Console.Write(item.Rank);
        }
        // Prints 345
    }

我理解变量r被捕获(闭包)并在第二次调用时重用。我不希望这种行为。是否有任何干净的方法来计算排名并分配它? 此外,r变量(在实际代码中)与foreach循环存在的范围不同。它在一个返回var first

的函数中

3 个答案:

答案 0 :(得分:5)

var first = myArray.Select((s, i) => { s.Rank = i; return s; });

答案 1 :(得分:3)

LINQ使用延迟评估并在每次使用Select时运行myArray部分。

通过将结果存储在List

,您可以强制进行一次评估

更改

var first = myArray.Select(s => { s.Rank = r++; return s; });

var first = myArray.Select(s => { s.Rank = r++; return s; }).ToList();

另一种方法是每次使用myArray加入Zip新序列,例如

var first = myArray.Zip(Enumerable.Range(0, int.MaxValue), (s, r) =>
{
    s.Rank = r;
    return s;
});

答案 2 :(得分:1)

如果您没有查询集合,则不应使用LINQ。

要更新数组中的每个项目,请使用for循环:

for (int i = 0; i < myArray.Length; i++)
{
    myArray[i].Rank = i;
}

要更新可枚举项中的每个项目,请使用foreach循环:

int r = 0;
foreach (var item in myArray)
{
    item.Rank = r++;
}