C#中foreach循环中的计数器

Counter in foreach loop in C#

前臂工作:正如我所知,

foreach is a loop which iterates through a collection or array one by
one, starting from 0 index till the
last item of the collection.

所以,如果数组中有n个项。

1
2
3
4
foreach (var item in arr)
{

}

然后,在第1次迭代中,item=arr[0];然后,在第二个,item=arr[1];...在最后(nth),item=arr[n-1];

结论:通过工作,似乎在每次迭代中,它都知道要从数组中获取哪个值,或者它知道要从数组中获取的项的索引。

现在我的问题是:如何在不使用新变量的情况下获得一个项目的索引?

1
2
3
4
5
6
7
8
foreach (string item in mylist)
{
   if (item =="myitem")
   {
       // get index of item
       break;
   }
}


这取决于你所说的"它"是什么意思。迭代器知道它到达了什么索引,是的——在List或数组的情况下。但在IEnumerator中没有一般指数。它是否在索引集合上迭代取决于实现。许多集合不支持直接索引。

(实际上,foreach并不总是使用迭代器。如果集合的编译时类型是一个数组,编译器将使用array[0]array[1]等对其进行迭代。同样,集合也可以有一个名为GetEnumerator()的方法,该方法返回具有适当成员的类型,但不需要看到IEnumerableIEnumerator的任何实现。)

维护索引的选项:

  • 使用for循环
  • 使用单独的变量
  • 使用将每个项投影到索引/值对的投影,例如

    1
    2
    3
    4
     foreach (var x in list.Select((value, index) => new { value, index }))
     {
         // Use x.value and x.index in here
     }
  • 使用我的SmartEnumerable类,这有点像前面的选项。

除了第一个选项以外,其他所有选项都可以工作,不管集合是否自然索引。


for代替foreachforeach不公开其内部工作,它列举了IEnumerable的任何内容(根本不需要索引)。

1
2
3
4
for (int i=0; i<arr.Length; i++)
{
    ...
}

此外,如果您要做的是在列表中找到一个特定项的索引,那么您完全不需要自己迭代它。用Array.IndexOf(item)代替。


您对EDOCX1 1的理解是不完整的。

它适用于公开EDCOX1×3(或实现EDCOX1×7)方法的任何类型,并使用返回的EDCOX1引用8迭代来遍历集合中的项。

EDOCX1 9如何使用这个索引(使用索引、EDCOX1、10、语句或魔术)是一个实现细节。

为了实现你想要的,你应该使用EDCOX1×0循环:

1
2
3
for (int i = 0; i < mylist.Count; i++)
{
}

注:

根据列表的类型,获取列表中的项目数略有不同

1
2
3
For Collections: Use Count   [property]
For Arrays:      Use Length  [property]
For IEnumerable: Use Count() [Linq method]


或者更简单,如果您不想使用大量的LINQ,并且出于某种原因不想使用for循环。

1
2
3
4
5
6
int i = 0;
foreach(var x in arr)
{
   //Do some stuff
   i++;
}


可能毫无意义,但是…

1
2
3
4
foreach (var item in yourList.Select((Value, Index) => new { Value, Index }))
{
    Console.WriteLine("Value=" + item.Value +", Index=" + item.Index);
}

不带自定义foreach版本:

1
2
3
4
5
datas.Where((data, index) =>
{
    //Your Logic
    return false;
}).Any();

在一些简单的例子中,我的方法是使用where + false + any。它比foreach + select((data,index)=>new{data,index})稍胖,而且没有传统的foreach方法。

MyLogic:

  • use statement body run your logic.
  • because return false,new Enumrable data count is zero.
  • use Any() let yeild run.

基准测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
[RPlotExporter, RankColumn]
public class BenchmarkTest
{
    public static IEnumerable<dynamic> TestDatas = Enumerable.Range(1, 10).Select((data, index) => $"item_no_{index}");

    [Benchmark]
    public static void ToArrayAndFor()
    {
        var datats = TestDatas.ToArray();
        for (int index = 0; index < datats.Length; index++)
        {
            var result = $"{datats[index]}{index}";
        }
    }

    [Benchmark]
    public static void IEnumrableAndForach()
    {
        var index = 0;
        foreach (var item in TestDatas)
        {
            index++;
            var result = $"{item}{index}";
        }
    }

    [Benchmark]
    public static void LinqSelectForach()
    {
        foreach (var item in TestDatas.Select((data, index) => new { index, data }))
        {
            var result = $"{item.data}{item.index}";
        }
    }

    [Benchmark]
    public static void LinqSelectStatementBodyToList()
    {
        TestDatas.Select((data, index) =>
        {
            var result = $"{data}{index}";
            return true;
        }).ToList();
    }

    [Benchmark]
    public static void LinqSelectStatementBodyToArray()
    {
        TestDatas.Select((data, index) =>
        {
            var result = $"{data}{index}";
            return true;
        }).ToArray();
    }

    [Benchmark]
    public static void LinqWhereStatementBodyAny()
    {
        TestDatas.Where((data, index) =>
        {
            var result = $"{data}{index}";
            return false;
        }).Any();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var summary = BenchmarkRunner.Run<BenchmarkTest>();

        System.Console.Read();
    }
}

基准结果:

1
2
3
4
5
6
7
8
                         Method |     Mean |     Error |    StdDev | Rank |
------------------------------- |---------:|----------:|----------:|-----:|
                  ToArrayAndFor | 4.027 us | 0.0797 us | 0.1241 us |    4 |
            IEnumrableAndForach | 3.494 us | 0.0321 us | 0.0285 us |    1 |
               LinqSelectForach | 3.842 us | 0.0503 us | 0.0471 us |    3 |
  LinqSelectStatementBodyToList | 3.822 us | 0.0416 us | 0.0389 us |    3 |
 LinqSelectStatementBodyToArray | 3.857 us | 0.0764 us | 0.0785 us |    3 |
      LinqWhereStatementBodyAny | 3.643 us | 0.0693 us | 0.0712 us |    2 |


只有当您在迭代数组时,这才是正确的;如果您在迭代一种不同类型的集合,而该集合没有按索引访问的概念,该怎么办?在数组的情况下,保持索引的最简单方法是简单地使用一个普通的for循环。


来自MSDN:

The foreach statement repeats a group
of embedded statements for each
element in an array or an object
collection that implements the
System.Collections.IEnumerable or
System.Collections.Generic.IEnumerable(Of
T) interface.

所以,它不一定是数组。它甚至可能是一个懒惰的集合,不知道集合中项目的计数。


在foreach循环中迭代的序列可能不支持索引或不知道这样一个概念,它只需要实现一个名为getEnumerator的方法,该方法返回一个对象,该对象至少具有IEnumerator接口,但不需要实现它。如果您知道您迭代的内容支持索引,并且您需要索引,那么我建议使用for循环。

可在foreach中使用的示例类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    class Foo {

        public iterator GetEnumerator() {
            return new iterator();
        }

        public class iterator {
            public Bar Current {
                get{magic}
            }

            public bool MoveNext() {
                incantation
            }
        }
    }


并非所有集合都有索引。例如,我可以将Dictionaryforeach一起使用(并遍历所有键和值),但我不能使用dictionary[0]dictionary[1]等编写get-a t单个元素。

如果我确实想遍历一个字典并跟踪一个索引,我就必须使用一个单独的变量,这个变量是我自己增加的。