如何获取foreach循环当前迭代的索引?


938

在C#中是否有一些我没遇到过的稀有语言构造(例如我最近学到的一些,有些是关于Stack Overflow的)来获取代表foreach循环当前迭代的值?

例如,我目前根据情况执行以下操作:

int i = 0;
foreach (Object o in collection)
{
    // ...
    i++;
}

1
对于我来说,foreach强制转换检索通常不会比仅对集合使用基于索引的访问优化,尽管在许多情况下,它是相同的。foreach的目的是使您的代码可读,但是(通常)添加了一层间接的操作,这不是免费的。
Brian

8
我想说的主要目的foreach是为所有集合提供通用的迭代机制,无论它们是否可索引ListDictionary)。
Brian Gideon

2
嗨,布莱恩·吉迪恩(Brian Gideon)-绝对同意(这是几年前的事,当时我经验不足)。但是,虽然Dictionary不可索引,Dictionary但它的迭代确实以特定顺序遍历它(即,枚举数可通过其顺序生成元素的事实而可索引)。从这个意义上讲,我们可以说不是在集合中寻找索引,而是在枚举中寻找当前枚举元素的索引(即我们是在第一个还是第五个或最后一个枚举元素上)。
马特·米切尔

4
foreach还允许编译器跳过边界,以检查已编译代码中的每个数组访问。将for与索引一起使用将使运行时检查您的索引访问是否安全。
IvoTops 2012年

1
但这是错误的。如果您不更改循环内for循环的迭代变量,则编译器将知道其边界,并且无需再次检查边界。这种情况很常见,任何体面的编译器都可以实现。
Jim Balter

Answers:


549

foreach是遍历实现集合IEnumerable。它通过调用GetEnumerator集合来实现,该集合将返回Enumerator

此枚举器具有一个方法和一个属性:

  • MoveNext()
  • 当前

Current返回枚举器当前所在的对象,并MoveNext更新Current到下一个对象。

索引的概念与枚举的概念是陌生的,无法做到。

因此,大多数集合都可以使用索引器和for循环结构遍历。

与使用局部变量跟踪索引相比,在这种情况下,我非常喜欢使用for循环。


165
“显然,索引的概念与枚举的概念是陌生的,无法做到。” -这是胡说八道,正如David B和bcahill的答案所阐明的那样。索引是一个范围内的枚举,没有理由不能并行枚举两件事……这正是Enumerable.Select的索引形式所做的。
吉姆·

11
基本代码示例:for(var i = 0; i < myList.Count; i ++){System.Diagnostics.Debug.WriteLine(i);}
乍得Hedgcock 2015年

22
@JimBalter:这是我阅读的第一个链接。我看不出它如何支持您的职位。回复时,我也不会向别人撒谎和分裂言论。我举一个例子,您使用“胡说八道”,“非常混乱”,“完全错误和困惑”,“我得到37票赞成”等。我想礼貌地要求您不要用“论证广告”来吓others别人,而我想鼓励您想办法在StackOverflow上为人们提供支持。在这方面,我想说乔恩·斯基特(Jon Skeet)是模范公民。
椒盐脆饼

11
椒盐脆饼:您将乔恩·斯凯特(Jon Skeet)当作模范公民使用是错误的,因为据我所知,他会粗暴对待(并投票)不同意他的人。吉姆·巴尔特(Jim Balter):您的某些评论过于激进,您本可以以较少的愤怒和更多的教育来表达自己的观点。
Suncat2000

7
@Pretzel Jim的观点是,只要您可以将元素映射到整数序列,就可以对其进行索引。该类本身不存储索引是不重要的。另外,链接列表确实有命令只能增强Jim的位置。您需要做的就是按顺序编号每个元素。具体来说,您可以通过迭代时增加计数来实现此目的,或者可以生成具有相同长度的整数列表,然后压缩它们(如Python zip函数中一样)。
jpmc26 2015年

665

Ian Mercer在Phil Haack的博客上发布了与此类似的解决方案:

foreach (var item in Model.Select((value, i) => new { i, value }))
{
    var value = item.value;
    var index = item.i;
}

这为您提供了物品(item.valueitem.i通过使用LINQ的这种重载,Select)及其索引():

函数[inside Select]中的第二个参数表示源元素的索引。

new { i, value }是创建一个新的匿名对象

通过使用可以避免堆分配 ValueTuple如果您使用的是C#7.0或更高版本,则:

foreach (var item in Model.Select((value, i) => ( value, i )))
{
    var value = item.value;
    var index = item.i;
}

您还可以item.使用自动解构来消除:

<ol>
foreach ((MyType value, Int32 i) in Model.Select((value, i) => ( value, i )))
{
    <li id="item_@i">@value</li>
}
</ol>

9
对于Razor模板而言,该解决方案是不错的选择,其中模板的整洁度是一个不重要的设计问题,并且您还希望使用所枚举的每个项目的索引。但是,请记住,“包装”的对象分配会在(不可避免的)整数递增的基础上增加开销(在空间和时间上)。
大卫·布洛克

6
@mjsr文档在这里
Thorkil Holm-Jacobsen

19
抱歉-这很聪明,但是它真的比在foreach外部创建索引并在每个循环中递增索引更具可读性吗?
jbyrd

13
在更高的C#版本中,您还可以使用元组,因此您将具有以下内容:foreach(Model.Select((v,i)=>(v,i)))中的var(item,i)使用元组解构直接在for循环内访问项目和索引(i)。
Haukman

5
有人可以向我解释为什么这是一个很好的答案(在撰写本文时,有450多个投票)?据我所知,它比简单地增加一个计数器更难理解,因此难以维护,它使用更多的内存,而且速度可能较慢。我想念什么吗?
Rich N

182

最后,C#7具有一种不错的语法,可以在foreach循环内获取索引(即元组):

foreach (var (item, index) in collection.WithIndex())
{
    Debug.WriteLine($"{index}: {item}");
}

需要一些扩展方法:

public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self)       
   => self.Select((item, index) => (item, index)); 

8
这个答案被低估了,让元组更干净
Todd

7
修改为处理public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self) => self?.Select((item, index) => (item, index)) ?? new List<(T, int)>();
空集合

好一个。我最喜欢这种解决方案。
FranzHuber19年

这是最好的答案
w0ns88

2
Enumerated对于习惯于其他语言的人来说,调用该方法可能更有用(也许也可以交换元组参数的顺序)。WithIndex无论如何,这并不是显而易见的。
FernAndr

114

可以做这样的事情:

public static class ForEachExtensions
{
    public static void ForEachWithIndex<T>(this IEnumerable<T> enumerable, Action<T, int> handler)
    {
        int idx = 0;
        foreach (T item in enumerable)
            handler(item, idx++);
    }
}

public class Example
{
    public static void Main()
    {
        string[] values = new[] { "foo", "bar", "baz" };

        values.ForEachWithIndex((item, idx) => Console.WriteLine("{0}: {1}", idx, item));
    }
}

12
那并不能“真正”解决问题。这个主意很好,但不能避免增加计数变量
Atmocreations

如果我们在for循环中有一个return语句,这将不起作用,如果您为此更改了“ ForEachWithIndex”,则它不是通用的,最好编写一个常规的for循环
Shankar Raju 2012年

使用包含字符串和索引的Linq Select,您的ForEachWithIndex调用等效于此调用:values.Select((item, idx) => { Console.WriteLine("{0}: {1}", idx, item); return item; }).ToList();
user2023861 2016年

95

我不同意for在大多数情况下循环是一个更好的选择的评论。

foreach是一个有用的构造,并非for在所有情况下都可以被循环替换。

例如,如果您有一个DataReader并使用a遍历所有记录,foreach它将自动调用Dispose方法并关闭阅读器(然后可以自动关闭连接)。因此,这样做更安全,因为即使您忘记关闭阅读器,它也可以防止连接泄漏。

(确保始终关闭读者是一个好习惯,但是如果您不这样做,则编译器不会捕获它-您不能保证已经关闭了所有读者,但是可以通过以下方式使自己更有可能不会泄漏连接:养成使用foreach的习惯。)

可能存在其他示例,该Dispose方法的隐式调用很有用。



+1。我详细地介绍了如何编写更多的foreach是从不同的for(和接近while)上Programmers.SE
Arseni Mourzenko 2013年

64

字面解释-警告,因为只是使用性能可能不如好int跟踪指数。至少它比使用更好IndexOf

您只需要使用Select的索引过载,即可使用知道索引的匿名对象包装集合中的每个项目。可以对实现IEnumerable的任何对象执行此操作。

System.Collections.IEnumerable collection = Enumerable.Range(100, 10);

foreach (var o in collection.OfType<object>().Select((x, i) => new {x, i}))
{
    Console.WriteLine("{0} {1}", o.i, o.x);
}

3
使用OfType <T>()而不是Cast <T>()的唯一原因是,枚举中的某些项目可能无法通过显式强制转换。对于对象,永远不会这样。
dahlbyk,2009年

13
当然,除了使用OfType而不是Cast的其他原因外-这是我从不使用Cast。
艾米B

36

使用LINQ,C#7和System.ValueTupleNuGet包,您可以执行以下操作:

foreach (var (value, index) in collection.Select((v, i)=>(v, i))) {
    Console.WriteLine(value + " is at index " + index);
}

您可以使用常规foreach构造,并且可以直接访问值和索引,而不必作为对象的成员,并且只能将两个字段保留在循环范围内。由于这些原因,我相信如果您能够使用C#7和,则这是最好的解决方案System.ValueTuple



@EdwardBrey我想不是。这个问题有很多答案,我可能只是想念它,或者没有注意到它在做什么,因为他将一些逻辑分离为扩展方法。
帕维尔

1
这是不同的,因为.Select是LINQ内置的。您不必编写自己的函数吗?不过,您将需要VS安装“ System.ValueTuple”。
安东

33

使用@FlySwat的答案,我想出了以下解决方案:

//var list = new List<int> { 1, 2, 3, 4, 5, 6 }; // Your sample collection

var listEnumerator = list.GetEnumerator(); // Get enumerator

for (var i = 0; listEnumerator.MoveNext() == true; i++)
{
  int currentItem = listEnumerator.Current; // Get current item.
  //Console.WriteLine("At index {0}, item is {1}", i, currentItem); // Do as you wish with i and  currentItem
}

您使用获取枚举数GetEnumerator,然后使用循环进行for循环。但是,诀窍是使循环成为条件listEnumerator.MoveNext() == true

由于MoveNext枚举器的方法在存在下一个元素时可以返回true,并且可以访问它,因此当我们用尽元素进行迭代时,循环条件使循环停止。


10
无需比较listEnumerator.MoveNext()== true。这就像问计算机是否为true == true?:)只要说一下listEnumerator.MoveNext(){}
请单击此处,

9
@Zesty,您绝对正确。我觉得在这种情况下添加它更具可读性,特别是对于那些不习惯输入i <blahSize作为条件的人。
Gezim

@Gezim我在这里不介意谓词,但我的意思是,它看起来并不像谓词。
John Dvorak

1
您应该处置枚举器。
安东宁Lejsek

@AntonínLejsek枚举器未实现IDisposable
爱德华·布雷

27

使用计数器变量没有错。事实上,无论你用forforeach while或者do,计数器变量必须某处声明和递增。

因此,如果不确定不确定是否有适当索引的集合,请使用以下惯用法:

var i = 0;
foreach (var e in collection) {
   // Do stuff with 'e' and 'i'
   i++;
}

否则,使用这一个,如果你知道你的可转位集合是O(1)索引访问(这将是Array,可能为List<T>(文档不说了),但不一定适合其他类型(如LinkedList)):

// Hope the JIT compiler optimises read of the 'Count' property!
for (var i = 0; i < collection.Count; i++) {
   var e = collection[i];
   // Do stuff with 'e' and 'i'
}

永远不需要IEnumerator通过调用MoveNext()和询问来“手动”操作Current- foreach节省了您的麻烦……如果您需要跳过项目,只需continue在循环体中使用a 。

为了完整起见,根据您对索引所做的操作(上述结构提供了很大的灵活性),您可以使用Parallel LINQ:

// First, filter 'e' based on 'i',
// then apply an action to remaining 'e'
collection
    .AsParallel()
    .Where((e,i) => /* filter with e,i */)
    .ForAll(e => { /* use e, but don't modify it */ });

// Using 'e' and 'i', produce a new collection,
// where each element incorporates 'i'
collection
    .AsParallel()
    .Select((e, i) => new MyWrapper(e, i));

我们使用AsParallel()上面的方法,因为已经是2014年了,我们希望充分利用这些多核来加快处理速度。此外,对于“顺序” LINQ,您只能ForEach()List<T>Array ... 获得扩展方法,而且尚不清楚使用它比执行简单方法要好foreach,因为您仍在使用单线程处理较丑陋的语法。


26

您可以将原始的枚举数与另一个包含索引信息的枚举数包装在一起。

foreach (var item in ForEachHelper.WithIndex(collection))
{
    Console.Write("Index=" + item.Index);
    Console.Write(";Value= " + item.Value);
    Console.Write(";IsLast=" + item.IsLast);
    Console.WriteLine();
}

这是ForEachHelper该类的代码。

public static class ForEachHelper
{
    public sealed class Item<T>
    {
        public int Index { get; set; }
        public T Value { get; set; }
        public bool IsLast { get; set; }
    }

    public static IEnumerable<Item<T>> WithIndex<T>(IEnumerable<T> enumerable)
    {
        Item<T> item = null;
        foreach (T value in enumerable)
        {
            Item<T> next = new Item<T>();
            next.Index = 0;
            next.Value = value;
            next.IsLast = false;
            if (item != null)
            {
                next.Index = item.Index + 1;
                yield return item;
            }
            item = next;
        }
        if (item != null)
        {
            item.IsLast = true;
            yield return item;
        }            
    }
}

这实际上不会返回该项目的索引。相反,它将返回枚举列表内的索引,该列表可能只是列表的子列表,从而仅在子列表和列表大小相等时才为您提供准确的数据。基本上,任何时候只要集合中包含对象而不是所请求的类型,您的索引都是不正确的。
卢卡斯B

7
@Lucas:否,但是它将返回当前foreach迭代的索引。这就是问题。
Brian Gideon

20

这是我刚想出的解决方案

原始代码:

int index=0;
foreach (var item in enumerable)
{
    blah(item, index); // some code that depends on the index
    index++;
}

更新的代码

enumerable.ForEach((item, index) => blah(item, index));

扩展方式:

    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T, int> action)
    {
        var unit = new Unit(); // unit is a new type from the reactive framework (http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx) to represent a void, since in C# you can't return a void
        enumerable.Select((item, i) => 
            {
                action(item, i);
                return unit;
            }).ToList();

        return pSource;
    }

16

只需添加您自己的索引。把事情简单化。

int i = 0;
foreach (var item in Collection)
{
    item.index = i;
    ++i;
}

15

它仅适用于List,而不适用于任何IEnumerable,但是在LINQ中是这样的:

IList<Object> collection = new List<Object> { 
    new Object(), 
    new Object(), 
    new Object(), 
    };

foreach (Object o in collection)
{
    Console.WriteLine(collection.IndexOf(o));
}

Console.ReadLine();

@乔纳森我没有说这是一个很好的答案,我只是说这只是表明有可能做到他所要求的:)

@Graphain我不希望它很快-我不完全确定它是如何工作的,它每次可以在整个列表中重复以找到一个匹配的对象,这将是一个比较对象。

也就是说,List可以保留每个对象的索引以及计数。

乔纳森(Jonathan)是否有一个更好的主意?

最好只统计一下自己在哪个方面的优势,更简单,更适应。


5
不确定是否需要大量投票。当然,性能使其无法满足要求,但您确实回答了问题!
马特·米切尔

4
另一个问题是,仅当列表中的项目是唯一的时,它才有效。
CodesInChaos 2011年

14

C#7最终为我们提供了一种优雅的方法:

static class Extensions
{
    public static IEnumerable<(int, T)> Enumerate<T>(
        this IEnumerable<T> input,
        int start = 0
    )
    {
        int i = start;
        foreach (var t in input)
        {
            yield return (i++, t);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var s = new string[]
        {
            "Alpha",
            "Bravo",
            "Charlie",
            "Delta"
        };

        foreach (var (i, t) in s.Enumerate())
        {
            Console.WriteLine($"{i}: {t}");
        }
    }
}

是的,MS应该扩展CLR / BCL来使这种事情变得本地化。
Todd

14

为什么要foreach?

如果使用List,最简单的方法是使用for而不是foreach :

for (int i = 0 ; i < myList.Count ; i++)
{
    // Do something...
}

或者,如果您要使用foreach:

foreach (string m in myList)
{
     // Do something...
}

您可以使用它来了解每个循环的索引:

myList.indexOf(m)

8
indexOf解决方案对于具有重复项的列表无效,并且速度非常慢。
tymtam '16

2
要避免的问题是您多次遍历IEnumerable,例如,获取项目数,然后获取每个项目。例如,当IEnumerable是数据库查询的结果时,这具有含义。
大卫·克拉克

2
myList.IndexOf()为O(n),因此您的循环将为O(n ^ 2)。
Patrick Beard

9
int index;
foreach (Object o in collection)
{
    index = collection.indexOf(o);
}

这将为收藏支持工作IList


68
两个问题:1)这是O(n^2)因为在大多数实现中IndexOfO(n)。2)如果列表中有重复的项目,此操作将失败。
CodesInChaos

15
注意:O(n ^ 2)意味着对于大集合而言,这可能会造成灾难性的缓慢。
O'Rooney 2011年

使用IndexOf方法的伟大发明!这就是我想要在foreach循环中获取索引(数字)的原因!Big Thx
Mitja Bonca

19
天哪,希望您不要使用!:(它确实使用了您不想创建的变量-实际上,它会创建n + 1个整数,因为该函数也必须创建一个整数才能返回,并且-搜索索引比创建索引慢得多每个步骤都进行一个整数递增运算。为什么人们不会对此答案投反对票?
canahari

13
不要使用这个答案,我发现其中一条评论中提到的确凿的事实。“如果列表中有重复的项目,此操作将失败。”
布鲁斯

9

这就是我的操作方式,这很简单/简洁,但是如果您在循环主体中做很多事情obj.Value,它将很快变得过时。

foreach(var obj in collection.Select((item, index) => new { Index = index, Value = item }) {
    string foo = string.Format("Something[{0}] = {1}", obj.Index, obj.Value);
    ...
}

8

这个答案:游说C#语言团队以获得直接的语言支持。

主要答案指出:

显然,索引的概念与枚举的概念是陌生的,无法做到。

尽管当前的C#语言版本(2020)确实如此,但这不是CLR /语言的概念限制,可以做到这一点。

Microsoft C#语言开发团队可以通过添加对新Interface IIndexedEnumerable的支持来创建新的C#语言功能。

foreach (var item in collection with var index)
{
    Console.WriteLine("Iteration {0} has value {1}", index, item);
}

//or, building on @user1414213562's answer
foreach (var (item, index) in collection)
{
    Console.WriteLine("Iteration {0} has value {1}", index, item);
}

如果foreach ()使用并且with var index存在,则编译器期望item集合声明IIndexedEnumerable接口。如果不存在该接口,则编译器可以使用IndexedEnumerable对象对源进行polyfill包装,该对象添加了用于跟踪索引的代码。

interface IIndexedEnumerable<T> : IEnumerable<T>
{
    //Not index, because sometimes source IEnumerables are transient
    public long IterationNumber { get; }
}

以后,可以将CLR更新为具有内部索引跟踪,这仅with在指定了关键字且源代码未直接实现的情况下使用IIndexedEnumerable

为什么:

  • Foreach看起来更好,并且在业务应用程序中,foreach循环很少是性能瓶颈
  • Foreach在内存上可以更有效。具有功能管道,而不是在每个步骤都转换为新集合。当CPU缓存故障较少且垃圾回收较少时,谁在乎它是否使用更多的CPU周期?
  • 要求编码器添加索引跟踪代码,破坏美观
  • 它很容易实现(请Microsoft),并且向后兼容

虽然大多数人在这里不是微软的员工,这是一个正确的答案,你可以游说微软加入这样的功能。您已经可以使用扩展功能构建自己的迭代器并使用元组,但是Microsoft可以撒上语法糖以避免扩展功能


等等,这个语言功能是否已经存在,或者将来会建议使用?
帕维尔

1
@Pavel我更新了答案以使其更清楚。提供此答案是为了与主要答案相反,该主要答案指出:“显然,索引的概念与枚举的概念无关,并且无法完成。”
Todd

6

如果集合是列表,则可以使用List.IndexOf,如下所示:

foreach (Object o in collection)
{
    // ...
    @collection.IndexOf(o)
}

13
现在算法是O(n ^ 2)(如果还不算差的话)。在使用此功能之前,我会非常仔细地考虑。它也是@crucible答案的副本
BradleyDotNET

1
@BradleyDotNET完全正确,请勿使用此版本。
奥斯卡(Oskar)

1
注意这一点!如果您的清单中有重复的商品,它将获得第一个的位置!
Sonhja

5

最好使用这样的关键字continue安全构造

int i=-1;
foreach (Object o in collection)
{
    ++i;
    //...
    continue; //<--- safe to call, index will be increased
    //...
}

5

您可以这样编写循环:

var s = "ABCDEFG";
foreach (var item in s.GetEnumeratorWithIndex())
{
    System.Console.WriteLine("Character: {0}, Position: {1}", item.Value, item.Index);
}

添加以下结构和扩展方法之后。

struct和扩展方法封装了Enumerable.Select功能。

public struct ValueWithIndex<T>
{
    public readonly T Value;
    public readonly int Index;

    public ValueWithIndex(T value, int index)
    {
        this.Value = value;
        this.Index = index;
    }

    public static ValueWithIndex<T> Create(T value, int index)
    {
        return new ValueWithIndex<T>(value, index);
    }
}

public static class ExtensionMethods
{
    public static IEnumerable<ValueWithIndex<T>> GetEnumeratorWithIndex<T>(this IEnumerable<T> enumerable)
    {
        return enumerable.Select(ValueWithIndex<T>.Create);
    }
}

3

我对这个问题的解决方法是扩展方法 WithIndex()

http://code.google.com/p/ub-dotnet-utilities/source/browse/trunk/Src/Utilities/Extensions/EnumerableExtensions.cs

像这样使用

var list = new List<int> { 1, 2, 3, 4, 5, 6 };    

var odd = list.WithIndex().Where(i => (i.Item & 1) == 1);
CollectionAssert.AreEqual(new[] { 0, 2, 4 }, odd.Select(i => i.Index));
CollectionAssert.AreEqual(new[] { 1, 3, 5 }, odd.Select(i => i.Item));

struct将对(index,item)对使用a 。
CodesInChaos 2011年

3

出于兴趣,Phil Haack只是在Razor模板化委托(http://haacked.com/archive/2011/04/14/a-better-razor-foreach-loop.aspx

他有效地编写了一个扩展方法,该方法将迭代包装在“ IteratedItem”类中(请参见下文),从而允许在迭代期间访问索引以及元素。

public class IndexedItem<TModel> {
  public IndexedItem(int index, TModel item) {
    Index = index;
    Item = item;
  }

  public int Index { get; private set; }
  public TModel Item { get; private set; }
}

但是,尽管在非Razor环境中这是很好的,但是如果您执行单个操作(即可以作为Lambda提供的操作),但在非Razor上下文中,它不会永久替代for / foreach语法。



3

我在LINQPad中构建了这个:

var listOfNames = new List<string>(){"John","Steve","Anna","Chris"};

var listCount = listOfNames.Count;

var NamesWithCommas = string.Empty;

foreach (var element in listOfNames)
{
    NamesWithCommas += element;
    if(listOfNames.IndexOf(element) != listCount -1)
    {
        NamesWithCommas += ", ";
    }
}

NamesWithCommas.Dump();  //LINQPad method to write to console.

您也可以使用string.join

var joinResult = string.Join(",", listOfNames);

您也可以在c#中使用string.join,例如:var joinResult = string.Join(“,”,listOfNames);'
沃伦·拉弗朗

1
有人可以告诉我这是O(n * n)吗?
Axel

@Axel基本上意味着,计算结果所需的运算将平方增加,即,如果有n项目,则运算为n * n或- n平方。en.wikipedia.org/wiki/…–
理查德·汉塞尔

2

我不相信有一种方法可以获取foreach循环当前迭代的值。盘点自己,似乎是最好的方法。

请问,为什么您想知道?

似乎您大多数会想做以下三件事之一:

1)从集合中获取对象,但是在这种情况下,您已经拥有它。

2)计算对象以便以后进行后期处理...这些集合具有Count属性,您可以利用它。

3)根据对象在循环中的顺序为其设置属性...尽管您可以轻松地在将对象添加到集合时进行设置。


4)我多次击打的情况与第一遍或最后一遍必须完成的操作有所不同-例如要打印的对象列表,并且需要在项目之间使用逗号,但在最后一个项目之后不需要逗号。
罗伦·佩希特尔

2

除非您的集合可以通过某种方法返回对象的索引,否则唯一的方法就是使用示例中的计数器。

但是,使用索引时,对该问题的唯一合理答案是使用for循环。其他任何事情都会导致代码复杂性,更不用说时间和空间的复杂性了。


2

这样的事情怎么样?请注意,如果myEnumerable为空,则myDelimitedString可以为null。

IEnumerator enumerator = myEnumerable.GetEnumerator();
string myDelimitedString;
string current = null;

if( enumerator.MoveNext() )
    current = (string)enumerator.Current;

while( null != current)
{
    current = (string)enumerator.Current; }

    myDelimitedString += current;

    if( enumerator.MoveNext() )
        myDelimitedString += DELIMITER;
    else
        break;
}

这里有多个问题。A)额外的支撑。B)字符串concat + =每个循环迭代。
enorl76 2013年

2

我只是遇到了这个问题,但是在我的案例中考虑问题会提供最佳解决方案,而与预期解决方案无关。

这可能是一个很常见的情况,基本上,我正在从一个源列表中读取并在目标列表中基于它们创建对象,但是,我必须先检查源项是否有效,并想返回任何行错误。乍一看,我想将索引添加到Current属性的对象的枚举数中,但是,当我复制这些元素时,无论如何从当前目标位置我都隐式知道了当前索引。显然,这取决于您的目标对象,但对我而言,它是一个List,并且很可能将实现ICollection。

var destinationList = new List<someObject>();
foreach (var item in itemList)
{
  var stringArray = item.Split(new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries);

  if (stringArray.Length != 2)
  {
    //use the destinationList Count property to give us the index into the stringArray list
    throw new Exception("Item at row " + (destinationList.Count + 1) + " has a problem.");
  }
  else
  {
    destinationList.Add(new someObject() { Prop1 = stringArray[0], Prop2 = stringArray[1]});
  }
}

我认为,这并不总是适用,但经常值得一提。

无论如何,关键是有时您的逻辑中已经存在一个非显而易见的解决方案...


2

我不确定您要根据问题使用索引信息做什么。但是,在C#中,通常可以改写IEnumerable.Select方法以使索引脱离所需的范围。例如,对于一个值是奇数还是偶数,我可能会使用类似的东西。

string[] names = { "one", "two", "three" };
var oddOrEvenByName = names
    .Select((name, index) => new KeyValuePair<string, int>(name, index % 2))
    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

这将为您提供一个字典,按名称列出列表中的项目是奇数(1)还是偶数(0)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.