为什么这个可枚举列表分裂了?

本文关键字:分裂 列表 枚举 为什么 | 更新日期: 2023-09-27 18:17:47

我尝试一次从字符串列表中取出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只包含例如"字符串1",这是按字符分割的?!

我在这里错过了什么?

为什么这个可枚举列表分裂了?

但是当我运行这个currRows只包含例如"字符串1"和这是一个角色一个角色的分割吗!

这是因为Enumerable.Take将从IEnumerable<T>中获取所请求的项目数量。这使得您的x变量为string类型,稍后您将其称为ToList(),从而有效地创建了List<char>,这不是您想要的。

你可以使用MoreLINQ,它有一个Batch扩展方法,这正是你想要的。它返回一个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)
    {
    }
}

另一种可能性是自己创建该扩展方法。

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));
    }
}
  1. x.ToList();正在string上调用该方法。stringIEnumerable<char>,所以currRows的结果是一个字符列表。您可以完全删除该行,并在第二个循环中使用x
  2. 使用ToList()通常是[1]一个毫无意义的操作,如果你在foreach循环中使用的集合上做它。它将迭代您的集合并构造一个新的列表,然后迭代该列表。你可以从你的第一个循环中删除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。

从代码开始:

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只是listofstring的前三件事,所以我们有:

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)
    {
    }
}

现在你应该清楚为什么你会得到你所看到的行为。