简体   繁体   English

C#中foreach循环中的计数器

[英]Counter in foreach loop in C#

Working of foreach: As I know, foreach的工作:据我所知,

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. foreach是一个循环,它逐个遍历集合或数组,从0索引开始直到集合的最后一项。

So, if I have n items in an array. 所以,如果我在数组中有n个项目。

foreach (var item in arr)
{

}  

then, In, 1st iteration, item=arr[0]; 然后,在第一次迭代中, item = arr [0];
then, in 2nd, item=arr[1]; 然后,在第2, item = arr [1];
.
.
.
in last (nth), item=arr[n-1]; 在last(nth), item = arr [n-1];

Conclusion: from working it seems that at each iteration it knows that which value to be taken from array or it knows the index of the item to be taken from array. 结论:从工作开始,它似乎在每次迭代时都知道从数组中取出哪个值,或者它知道要从数组中获取的项的索引。

Now my question: How can I get index of an item without using a new variable? 现在我的问题是:如何在不使用新变量的情况下获取项目的索引?

foreach (string item in mylist)
{
   if (item == "myitem")
   {
       // get index of item
       break;
   }
}

It depends what you mean by "it". 这取决于你的意思“它”。 The iterator knows what index it's reached, yes - in the case of a List<T> or an array. 迭代器知道它到达了什么索引,是 - 在List<T>或数组的情况下。 But there's no general index within IEnumerator<T> . IEnumerator<T>没有通用索引。 Whether it's iterating over an indexed collection or not is up to the implementation. 它是否在索引集合上进行迭代取决于实现。 Plenty of collections don't support direct indexing. 大量集合不支持直接索引。

(In fact, foreach doesn't always use an iterator at all. If the compile-time type of the collection is an array, the compiler will iterate over it using array[0] , array[1] etc. Likewise the collection can have a method called GetEnumerator() which returns a type with the appropriate members, but without any implementation of IEnumerable / IEnumerator in sight.) (事实上​​, foreach并不总是使用迭代器。如果集合的编译时类型是一个数组,编译器将使用array[0]array[1]等迭代它。同样,集合可以有一个名为GetEnumerator()的方法,它返回一个具有相应成员的类型,但看不到任何IEnumerable / IEnumerator实现。)

Options for maintaining an index: 维护索引的选项:

  • Use a for loop 使用for循环
  • Use a separate variable 使用单独的变量
  • Use a projection which projects each item to an index/value pair, eg 使用投影将每个项目投影到索引/值对,例如

      foreach (var x in list.Select((value, index) => new { value, index })) { // Use x.value and x.index in here } 
  • Use my SmartEnumerable class which is a little bit like the previous option 使用我的SmartEnumerable类,它有点像以前的选项

All but the first of these options will work whether or not the collection is naturally indexed. 除了第一个选项外,其他所有选项都可以使用,无论该集合是否自然编入索引。

Use for instead of foreach . for代替foreach foreach doesn't expose its inner workings, it enumerates anything that is IEnumerable (which doesn't have to have an index at all). foreach不公开它的内部工作,它枚举任何IEnumerable (它根本没有索引)。

for (int i=0; i<arr.Length; i++)
{
    ...
}

Besides, if what you're trying to do is find the index of a particular item in the list, you don't have to iterate it at all by yourself. 此外,如果你要做的是找到列表中特定项目的索引,则不必自己迭代它。 Use Array.IndexOf(item) instead. 请改用Array.IndexOf(item)

Your understanding of foreach is incomplete. 您对foreach理解不完整。

It works with any type that exposes IEnumerable (or implements a GetEnumerable method) and uses the returned IEnumerator to iterate over the items in the collection. 它适用于暴露IEnumerable (或实现GetEnumerable方法)并使用返回的IEnumerator迭代集合中的项的任何类型。

How the Enumerator does this (using an index, yield statement or magic) is an implementation detail. Enumerator器如何执行此操作(使用索引, yield语句或魔术)是一个实现细节。

In order to achieve what you want, you should use a for loop: 为了达到你想要的效果,你应该使用for循环:

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

Note: 注意:

Getting the number of items in a list is slightly different depending on the type of list 获取列表中的项目数量会略有不同,具体取决于列表的类型

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

Or even more simple if you don't want to use a lot of linq and for some reason don't want to use a for loop. 或者更简单,如果你不想使用很多linq并且由于某种原因不想使用for循环。

int i = 0;
foreach(var x in arr)
{
   //Do some stuff
   i++;
}

Probably pointless, but... 可能毫无意义,但......

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

This is only true if you're iterating through an array; 只有在迭代数组时才会出现这种情况; what if you were iterating through a different kind of collection that has no notion of accessing by index? 如果您正在迭代不具有索引访问概念的不同类型的集合,该怎么办? In the array case, the easiest way to retain the index is to simply use a vanilla for loop. 在数组的情况下,保留索引的最简单方法是简单地使用vanilla for循环。

Without Custom Foreach Version: 没有自定义Foreach版本:

datas.Where((data, index) =>
{
    //Your Logic
    return false;
}).Any();

In some simple case,my way is using where + false + any . 在一些简单的例子中,我的方式是使用where + false + any
It is fater a little than foreach + select((data,index)=>new{data,index}) ,and without custom Foreach method. 它比foreach + select((data,index)=>new{data,index}) ,并且没有自定义的Foreach方法。

MyLogic: MyLogic:

  • use statement body run your logic. 使用语句体运行你的逻辑。
  • because return false ,new Enumrable data count is zero. 因为返回false ,新的可枚举数据计数为零。
  • use Any() let yeild run. 使用Any()让yeild运行。

Benchmark Test Code 基准测试代码

[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();
    }
}

Benchmark Result : 基准结果:

                         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 |

Not all collections have indexes. 并非所有集合都有索引。 For instance, I can use a Dictionary with foreach (and iterate through all the keys and values), but I can't write get at individual elements using dictionary[0] , dictionary[1] etc. 例如,我可以使用带foreachDictionary (并遍历所有键和值),但我不能使用dictionary[0]dictionary[1]等来编写单个元素。

If I did want to iterate through a dictionary and keep track of an index, I'd have to use a separate variable that I incremented myself. 如果我确实想要遍历字典并跟踪索引,我必须使用一个单独的变量,我自己增加。

The sequence being iterated in a foreach loop might not support indexing or know such a concept it just needs to implement a method called GetEnumerator that returns an object that as a minimum has the interface of IEnumerator though implmenting it is not required. 在foreach循环中迭代的序列可能不支持索引或知道这样一个概念,它只需要实现一个名为GetEnumerator的方法,该方法返回一个对象,该对象至少具有IEnumerator的接口,尽管不需要它。 If you know that what you iterate does support indexing and you need the index then I suggest to use a for loop instead. 如果您知道迭代的内容确实支持索引并且您需要索引,那么我建议使用for循环。

An example class that can be used in foreach : 可以在foreach使用的示例类:

    class Foo {

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

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

            public bool MoveNext() {
                incantation
            }
        }
    }

From MSDN: 来自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语句为数组中的每个元素或实现System.Collections.IEnumerable或System.Collections.Generic.IEnumerable(Of T)接口的对象集合重复一组嵌入式语句。

So, it's not necessarily Array. 所以,它不一定是Array。 It could even be a lazy collection with no idea about the count of items in the collection. 它甚至可能是一个懒惰的集合,不知道集合中的项目数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM