列出<t> .ForEach with index </t>

时间:2012-10-24 17:14:08

标签: c# linq generics

我正在尝试找到以下代码的LINQ等价物:

NameValueCollection nvc = new NameValueCollection();

List<BusinessLogic.Donation> donations = new List<BusinessLogic.Donation>();
donations.Add(new BusinessLogic.Donation(0, "", "", "");
donations.Add(new BusinessLogic.Donation(0, "", "", "");
donations.Add(new BusinessLogic.Donation(0, "", "", "");

for(var i = 0; i < donations.Count(); i++)
{
    // NOTE: item_number_ + i - I need to be able to do this
    nvc.Add("item_number_" + i, donations[i].AccountName);
}

我希望我可以使用类似的东西:

NameValueCollection nvc = new NameValueCollection();

List<BusinessLogic.Donation> donations = new List<BusinessLogic.Donation>();
donations.Add(new BusinessLogic.Donation(0, "", "", "");
donations.Add(new BusinessLogic.Donation(0, "", "", "");
donations.Add(new BusinessLogic.Donation(0, "", "", "");

donations.ForEach(x => nvc.Add("item_name_" + ??, x.AccountName);

但我还没有找到一种方法来确定循环所在的迭代。任何帮助将不胜感激!

9 个答案:

答案 0 :(得分:21)

LINQ没有ForEach方法,并且有充分的理由。 LINQ用于执行查询。它旨在从某些数据源获取信息。 旨在改变数据源。 LINQ查询不应该导致副作用,这正是你在这里做的。

List 具有ForEach方法,这就是您正在使用的方法。因为它实际上不在System.Linq命名空间中,所以它在技术上不是LINQ的一部分。

问题中的for循环没有任何问题。尝试以你想要的方式改变它是错误的(从良好的实践角度来看)。

Here是一个更详细讨论此事的链接。

现在,如果您想忽略该建议并使用ForEach方法,那么编写一个为该操作提供索引的方法并不难:

public static void ForEach<T>(this IEnumerable<T> sequence, Action<int, T> action)
{
    // argument null checking omitted
    int i = 0;
    foreach (T item in sequence)
    {
        action(i, item);
        i++;
    }
}

答案 1 :(得分:10)

这有点令人费解,并创建了一个中间集合,但是如何:

donations.Select((x, i) => new {Name = "item_name_" + i, x.AccountName})
    .ToList()
    .ForEach(x=> nvc.Add(x.Name, x.AccountName));

这使用the overload of Enumerable.Select which incorporates the index

我不得不说这样做没有什么可以获得的。您使用中间集合创建了更多的开销,并且IMHO在原始for循环中失去了可读性。

如果您愿意使用foreach循环代替List.ForEach,也可以跳过中间集合。请参阅@wageoghe's answer(强烈推荐)。

答案 2 :(得分:6)

如果你真的想使用List.ForEach,那很简单:

//[...]
int i=0;
donations.ForEach(x => nvc.Add("item_name_" + i++, x.AccountName);

答案 3 :(得分:3)

您是否有任何理由不使用Dictionary<string, string>,因为您的姓名/密钥似乎是唯一的?这会更快,您可以使用ToDictionary标准查询运算符。

另外,如果您确实希望使用扩展方法(尽管Servy说for循环是正确的解决方案),那么您可以编写自己的方法 - 请参阅here

答案 4 :(得分:2)

回答@lc的回答。

foreach (var x in donations.Select((d, i) => new {ItemName = "item_name_" + i, AccountName = d.AccountName}))
{
  nvc.Add(x.ItemName, x.AccountName);
}

答案 5 :(得分:1)

这是一篇旧文章,但在Google的排名很高,因此我认为更通用的方法是合适的。 (而且,我往往会忘记这是如何完成的,这意味着我每次都必须在Google上搜索...)

假设一个整数列表:

var values = new List<int>() { 2, 3, 4, 0x100, 74, 0xFFFF, 0x0F0F };

要遍历列表并具有索引,请执行以下操作:

values.Select((x, i) => new
{
    item = x,
    index = i
})
.ToList()
.ForEach(obj =>
{
    int value = obj.item;
    int valueIndex = obj.index;
});

答案 6 :(得分:1)

这也可以使用聚合来完成。检查以下示例:

var data = new[]
{
    "A",
    "B",
    "C",
    "D"
};

var count = data.Aggregate(0, (index, item) =>
{
    Console.WriteLine("[{0}] => '{1}'", index, item);

    return index + 1;
});

Console.WriteLine("Total items: {0}", count);

Aggregate在这里用作for语句。唯一的缺点是,您需要在每次迭代中将索引的值增加1并返回。

答案 7 :(得分:0)

我喜欢通过以下方式做到这一点:

NameValueCollection nvc = new NameValueCollection();

List<BusinessLogic.Donation> donations = new List<BusinessLogic.Donation>();
donations.Add(new BusinessLogic.Donation(0, "", "", ""));
donations.Add(new BusinessLogic.Donation(0, "", "", ""));
donations.Add(new BusinessLogic.Donation(0, "", "", ""));

Enumerable
    .Range(0, donations.Count())
    .ToList()
    .ForEach(i => nvc.Add("item_number_" + i, donations[i].AccountName));

答案 8 :(得分:-1)

试试这个 -

donations.ForEach(x =>
         {
             int index = donations.IndexOf(x);
             nvc.Add("item_name_" + index, x.AccountName);
         });