为什么这个可枚举列表会分开?

why does this enumerable list split up?

我尝试一次从字符串列表中取出 3 行,但它没有按预期工作。考虑这段代码...

var listOfStrings = new List<string>
{
    "String 1",
    "String 2",
    "String 3",
    "String 4",
    "String 5",
    "String 6"
};

foreach (var x in listOfStrings.Take(3).ToList())
{
    var currRows = x.ToList();

    // currRows should have 3 items
    foreach (var itm in currRows)
    {

    }
}

第一次 运行 我希望 currRows 有 3 个项目(字符串 1、2 和 3),第二次我希望有这 3 个项目(字符串 4、5 和 6)。但是当我 运行 这个 currRows 只包含例如 "String 1" 并且这是一个字符一个字符地拆分?!

我在这里错过了什么?

  1. x.ToList(); 正在调用 string 上的方法。 string 是一个 IEnumerable<char>,因此 currRows 的结果是一个字符列表。您可以完全删除该行,并在第二个循环中使用 x
  2. 如果您在 foreach 循环中使用的集合上使用 ToList() 通常 [1] 是毫无意义的操作。它将迭代您的集合并构建一个新列表,然后对其进行迭代。您可以从第一个循环中删除 ToList,它的性能会更好,但结果相同。
  3. Take 只需要 n 个项目,它不会按 n 个项目分组。

试试这个:

var listOfStrings = new List<string>
{
    "String 1",
    "String 2",
    "String 3",
    "String 4",
    "String 5",
    "String 6"
};

int step = 0;
const int numTake = 3;
for(var i = 0; i < listOfStrings.Count / numTake; i++)
{
    Console.WriteLine("Starting a group!");
    foreach (var x in listOfStrings.Skip(3 * step++).Take(3))
    {
        Console.WriteLine(x);
    }
}

有很大的改进空间,但它应该让您了解应该如何完成这项任务。

  1. 种情况下您需要这样做 - 但您在开始使用 LINQ 时很可能不会遇到它们。

But when I run this currRows only contains for example "String 1" and this is split up character by character?!

那是因为 Enumerable.Take 将从 IEnumerable<T> 中获取请求数量的项目。这使您的 x 变量成为 string 类型,您稍后会调用 ToList(),有效地创建一个 List<char>,这不是您想要的。

您可以使用 MoreLINQ,它有一个 Batch 扩展方法,可以完全满足您的需求。它 returns 一个 IEnumerable<IEnumerable<T>>:

foreach (var batch in listOfStrings.Batch(3))
{
    // batch is an IEnumerable<T>, and will have 3 items.
    foreach (var item in batch)
    {

    }
}

另一种可能性是您自己创建该扩展方法。这取自 this answer:

public static class EnumerableExtensions
{
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items,
                                                       int maxItems)
    {
        return items.Select((item, inx) => new { item, inx })
                    .GroupBy(x => x.inx / maxItems)
                    .Select(g => g.Select(x => x.item));
    }
}

从您的代码开始:

var listOfStrings = new List<string>
{
    "String 1",
    "String 2",
    "String 3",
    "String 4",
    "String 5",
    "String 6"
};

foreach (var x in listOfStrings.Take(3).ToList())
{
    var currRows = x.ToList();

    // currRows should have 3 items
    foreach (var itm in currRows)
    {

    }
}

让我们通过为要循环的内容创建一个额外的变量来稍微简化您的代码:

var listOfStrings = new List<string>
{
    "String 1",
    "String 2",
    "String 3",
    "String 4",
    "String 5",
    "String 6"
};

var thingsYouAreLoopingOver = listOfStrings.Take(3).ToList();

foreach (var x in thingsYouAreLoopingOver)
{
    var currRows = x.ToList();

    // currRows should have 3 items
    foreach (var itm in currRows)
    {

    }
}

但这肯定可以再次简化,因为 thingsYouAreLoopingOver 只是 listOfStrings 的前三项,所以我们有:

var thingsYouAreLoopingOver = new List<string>
{
    "String 1",
    "String 2",
    "String 3"
};

foreach (var x in thingsYouAreLoopingOver)
{
    var currRows = x.ToList();

    // currRows should have 3 items
    foreach (var itm in currRows)
    {

    }
}

现在应该清楚为什么会出现您所看到的行为了。