foreach 루프의 현재 반복 색인을 어떻게 얻습니까?


938

foreach 루프의 현재 반복을 나타내는 값을 얻기 위해 C #에서 내가 경험하지 않은 희귀 언어 구조 (최근에 배운 소수, 스택 오버플로에 대한 일부)가 있습니까?

예를 들어, 현재 상황에 따라 이와 같은 작업을 수행합니다.

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

1
foreach 캐스팅 검색은 일반적으로 컬렉션에서 인덱스 기반 액세스를 사용하는 것보다 더 최적화되지는 않지만 많은 경우에 동일합니다. foreach의 목적은 코드를 읽을 수있게 만드는 것이지만 일반적으로 간접 계층을 추가하여 무료가 아닙니다.
Brian

8
나는 주요한 목적은 foreach모든 콜렉션이 색인 가능한지 ( List) 여부에 관계없이 모든 콜렉션에 공통 반복 메커니즘을 제공하는 것 Dictionary입니다.
Brian Gideon

2
안녕 Brian Gideon-확실히 동의하십시오 (몇 년 전이었고 당시에는 경험이 훨씬 적었습니다). 그러나 Dictionary인덱싱 할 수는 없지만 반복은 Dictionary특정 순서로 순회합니다 (즉, 열거자는 요소를 순차적으로 생성한다는 사실에 의해 인덱싱 가능합니다). 이런 의미에서 컬렉션 내에서 인덱스를 찾는 것이 아니라 열거 내에서 현재 열거 된 요소의 인덱스를 찾는다고 말할 수 있습니다 (즉, 첫 번째 또는 다섯 번째 또는 마지막 열거 된 요소인지 여부).
Matt Mitchell

4
foreach는 또한 컴파일러가 컴파일 된 코드에서 각 배열 액세스를 검사하는 범위를 건너 뛸 수 있도록합니다. 인덱스와 함께 for를 사용하면 런타임에 인덱스 액세스가 안전한지 확인할 수 있습니다.
IvoTops

1
그러나 그것은 거짓입니다. 루프 내에서 for 루프의 반복 변수를 변경하지 않으면 컴파일러는 해당 경계가 무엇인지 알고이를 다시 확인할 필요가 없습니다. 이것은 괜찮은 컴파일러가 그것을 구현하는 일반적인 경우입니다.
Jim Balter

답변:


550

foreach구현 컬렉션을 통해 반복입니다 IEnumerable. GetEnumerator컬렉션 을 호출하여이 작업 을 수행하면을 반환합니다 Enumerator.

이 열거 자에는 메소드와 속성이 있습니다.

  • MoveNext ()
  • 흐름

Current열거자가 현재있는 개체를 반환하고 다음 개체로 MoveNext업데이트 Current합니다.

인덱스 개념은 열거 개념과는 다른 개념이므로 수행 할 수 없습니다.

이 때문에 대부분의 컬렉션은 인덱서와 for 루프 구문을 사용하여 순회 할 수 있습니다.

이 상황에서 로컬 변수로 색인을 추적하는 것과 비교하여 for 루프를 사용하는 것이 좋습니다.


165
"물론 인덱스의 개념은 열거의 개념과는 다른 개념이므로 수행 할 수 없습니다." -David B와 bcahill의 답변이 명확하기 때문에 이것은 말이되지 않습니다. 인덱스는 범위에 대한 열거이며, 두 가지를 병렬로 열거 할 수없는 이유는 없습니다. 이것이 바로 Enumerable.Select의 색인 형식입니다.
Jim Balter

11
기본 코드 예 :for(var i = 0; i < myList.Count; i ++){System.Diagnostics.Debug.WriteLine(i);}
Chad Hedgcock

22
@ JimBalter : 내가 읽은 첫 번째 링크입니다. 그것이 당신의 입장을 어떻게지지하는지 모르겠습니다. 또한 답장을 할 때 사람들에게 광고와 분열적인 단어를 던지지 않습니다. 나는 "넌센스", "비정상적인 혼란", "완전히 잘못되어 혼란스러워", "37 개의 공감대를 얻었습니다"등을 예로 들었습니다. '당신은'argumentum ad verecundiam '으로 다른 사람들을 눈여겨 보지 말고 정중하게 요청하고 싶습니다. 대신 StackOverflow에서 사람들을 지원할 수있는 방법을 생각하고 싶습니다. 존 스케이트 (Jon Skeet)는이 점에서 모범적 인 시민이라고 말하고 싶습니다.
Pretzel

11
Pretzel : Jon Skeet을 모범적 인 시민으로 사용하는 것은 내가 경험 한 것처럼 자신에 동의하지 않는 사람을 눈에 띄게 (그리고 공감) 잘못한 것입니다. Jim Balter : 귀하의 의견 중 일부는 지나치게 공격적이었으며 귀하는 더 적은 분노와 더 많은 교육으로 귀하의 요점을 제시 할 수있었습니다.
Suncat2000

7
@Pretzel Jim의 요점은 요소를 일련의 정수에 매핑 할 수 있으면 색인을 생성 할 수 있다는 것입니다. 클래스 자체가 인덱스를 저장하지 않는다는 것이 요점입니다. 또한, 연결리스트가 수행 순서가있는 경우에만 짐의 위치를 강화한다. 각 요소에 순서대로 번호를 매기면됩니다. 특히 반복 하는 동안 카운트 를 늘려서 이를 달성 하거나 동일한 길이의 정수 목록을 생성 한 다음 압축 할 수 있습니다 (파이썬 zip함수 에서와 같이 ).
jpmc26

666

Ian Mercer는 Phil Haack의 블로그 에 이와 유사한 솔루션을 게시했습니다 .

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

아이템을 얻습니다 (item.value LINQ의 오버로드를item.i 사용하여 )과 색인 ( )을 .Select

[inside Select] 함수의 두 번째 매개 변수는 소스 요소의 인덱스를 나타냅니다.

그만큼 new { i, value } 새로운 창조하고 익명의 개체를 .

다음을 사용하여 힙 할당을 피할 수 있습니다. ValueTupleC # 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 템플릿의 경우 유용합니다. 그러나 '래핑 (wrapping)'에서 객체를 할당하면 (피할 수없는) 정수 증분에 비용과 시간 및 공간이 추가된다는 점에 유의하십시오.
David Bullock


19
죄송합니다-영리하지만 foreach 외부에서 색인을 만들고 각 루프마다 증가시키는 것보다 실제로 더 읽기 쉽습니다?
jbyrd

13
이후의 C # 버전에서는 튜플도 사용하므로 다음과 같이됩니다 : foreach (Model.Select ((v, i) => (v, i))의 var (item, i)) 튜플 해체를 사용하여 for 루프 내부에서 직접 항목 및 색인 (i)에 액세스하십시오.
Haukman

5
누군가 이것이 왜 좋은 대답인지 설명 할 수 있습니까? 내가 알 수있는 한 단순히 카운터를 늘리는 것보다 이해하기가 어렵고 유지 보수가 적고 메모리를 많이 사용하며 속도가 느릴 수 있습니다. 뭔가 빠졌습니까?
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
null 수집을 처리하도록 수정되었습니다.public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self) => self?.Select((item, index) => (item, index)) ?? new List<(T, int)>();
2Toad

좋은데 나는이 솔루션을 가장 좋아합니다.
FranzHuber23

이것이 가장 좋은 대답입니다
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

ForEachWithIndex 호출은 문자열과 인덱스를 취하는 Linq Select를 사용하는 것과 같습니다.values.Select((item, idx) => { Console.WriteLine("{0}: {1}", idx, item); return item; }).ToList();
user2023861

95

for대부분의 경우 루프가 더 나은 선택 이라는 의견에 동의하지 않습니다 .

foreach유용한 구문이며 for모든 상황에서 루프로 대체 할 수 없습니다 .

예를 들어 DataReader가 있고 foreach이를 사용하여 모든 레코드를 반복하는 경우 자동으로 Dispose를 호출합니다. 메소드를 하고 리더를 닫습니다 (연결을 자동으로 닫을 수 있음). 따라서 리더를 닫는 것을 잊어 버린 경우에도 연결 누출을 방지하므로 더 안전합니다.

(항상 독자를 닫는 것이 좋지만 컴파일러가 그렇지 않은 경우 컴파일러가이를 포착하지는 않습니다. 모든 독자를 닫았다 고 보장 할 수는 없지만 연결을 통해 누수가 발생하지 않도록 할 수는 있습니다) foreach를 사용하는 습관.)

Dispose유용한 메소드 의 내재적 호출에 대한 다른 예가있을 수 있습니다 .


2
이것을 지적 해 주셔서 감사합니다. 오히려 미묘합니다. pvle.be/2010/05/foreach-statement-calls-dispose-on-ienumeratormsdn.microsoft.com/en-us/library/aa664754(VS.71).aspx 에서 자세한 정보를 얻을 수 있습니다 .
Mark Meuer

+1. 나는 방법에 대해 더 자세히 기록 된 foreach다른 for(그리고 가까이 while)에 Programmers.SE .
Arseni Mourzenko

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
Cast <T> () 대신 OfType <T> ()를 사용하는 유일한 이유는 열거의 일부 항목이 명시 적 캐스트에 실패 할 수 있기 때문입니다. 객체의 경우에는 절대 그렇지 않습니다.
dahlbyk 2016 년

13
물론 Cast 대신 OfType을 사용해야하는 다른 이유를 제외하고는 Cast를 사용하지 않는다는 것입니다.
Amy 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.


이것은 user1414213562의 답변과 어떻게 다릅니 까?
Edward Brey

@EdwardBrey 나는 그렇지 않다고 생각합니다. 이 질문에는 많은 답변이 있습니다. 아마도 그냥 놓쳤거나 논리의 일부를 확장 방법으로 분리했기 때문에 무슨 일이 있었는지 알지 못했습니다.
Pavel

1
.Select는 LINQ에 내장되어 있기 때문에 다릅니다. 당신은 당신의 자신의 함수를 작성할 필요가 없습니까? 그래도 VS에 "System.ValueTuple"을 설치해야합니다.
Anton

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
@ 제 스티, 당신은 절대적으로 맞습니다. 이 경우에는 i <blahSize 이외의 다른 조건을 입력하는 데 익숙하지 않은 사람들을 위해 추가하는 것이 더 읽기 쉽다고 생각했습니다.
Gezim

@Gezim 나는 술어를 신경 쓰지 않지만 이것이 술어처럼 보이지 않는다는 점을 지적합니다.
존 드보락

1
열거자를 폐기해야합니다.
Antonín Lejsek

@ AntonínLejsek 열거자는 구현하지 않습니다 IDisposable.
Edward Brey

27

카운터 변수를 사용하는 데 아무런 문제가 없습니다. 사실, 사용 여부 for, foreach 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루프의 본문에.

그리고 당신이 무엇에 따라 완전성을 위해 을 위해 인덱스 수행 한 작업 에 따라 (위의 구조는 많은 유연성을 제공합니다) 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;
        }            
    }
}

실제로 항목의 인덱스를 반환하지는 않습니다. 대신 목록의 하위 목록 일 수있는 열거 된 목록 내의 인덱스를 반환하므로 하위 목록과 목록의 크기가 동일한 경우에만 정확한 데이터를 제공합니다. 기본적으로 컬렉션에 요청 된 유형이 아닌 개체가있을 때마다 인덱스가 올바르지 않습니다.
Lucas 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

IEnumerable이 아닌 List에서만 작동하지만 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();

@Jonathan 나는 그것이 훌륭한 대답이라고 말하지 않았고, 방금 그가 요청한 것을 할 수 있음을 보여주고 있다고 말했다. :)

@Graphain 나는 그것이 빠를 것이라고 기대하지 않을 것입니다-그것이 어떻게 작동하는지 완전히 확신하지 못합니다. 매일 전체 목록을 반복하여 일치하는 객체를 찾을 때마다 비교할 수 있습니다.

즉, List는 개수와 함께 각 객체의 색인을 유지할 수 있습니다.

조나단이 더 정교하게 생각한다면 더 좋은 생각을 가지고있는 것 같습니까?

그러나 foreach의 위치, 단순성 및 적응성을 높이는 것이 좋습니다.


5
무거운 다운 보팅에 대해서는 확실하지 않습니다. 확실한 성능은 이것을 엄청나게 만들지 만 질문에 대답했습니다!
Matt Mitchell

4
이것의 또 다른 문제는 목록의 항목이 고유 한 경우에만 작동한다는 것입니다.
코드 InChaos

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를 사용하는 경우 가장 간단한 방법은 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

2
피해야 할 문제는 IEnumerable을 여러 번 탐색하는 경우입니다. 예를 들어 항목 수를 얻은 다음 각 항목을 가져옵니다. 이는 IEnumerable이 데이터베이스 쿼리의 결과 일 때 영향을 미칩니다.
David Clarke

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)대부분의 구현에서 IndexOf입니다 O(n). 2) 목록에 중복 된 항목이 있으면 실패합니다.
코드 InChaos

15
참고 : O (n ^ 2)는 큰 컬렉션의 경우 비참하게 느려질 수 있음을 의미합니다.
O'Rooney

IndexOf 방법을 사용하는 훌륭한 발명품! 이것이 foreach 루프에서 색인 (숫자)을 얻으려고했던 것입니다! Big Thx
Mitja Bonca

19
하나님, 당신이 그것을 사용하지 않기를 바랍니다! :( 그것은 당신이 만들고 싶지 않은 변수를 사용합니다-실제로, 그 함수도 반환하기 위해 하나를 생성해야하기 때문에 n + 1 정수를 생성합니다-그리고 검색 색인은 훨씬 느립니다. 모든 단계에서 하나의 정수 증분 연산 사람들이 왜이 답변에 투표하지 않습니까?
canahari

13
이 답변을 사용하지 마십시오, 나는 의견 중 하나에서 언급 된 어려운 진실을 발견했습니다. "목록에 중복 된 항목이 있으면 실패합니다."!!!
Bruce

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 # 언어 개발 팀은 새로운 인터페이스 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컴파일러는 항목 컬렉션이 IIndexedEnumerable인터페이스 를 선언 할 것으로 예상합니다 . 인터페이스가 없으면 컴파일러는 IndexedEnumerable 객체로 소스를 래핑하여 인덱스를 추적하기위한 코드를 추가 할 수 있습니다.

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 로비 할 수 있습니다. 이미 확장 기능을 사용 하여 자체 반복기를 빌드 하고 튜플을 사용할 수 있지만 Microsoft는 확장 기능을 피하기 위해 구문 설탕을 뿌릴 수 있습니다.


잠깐,이 언어 기능이 이미 존재합니까, 아니면 미래에 제안됩니까?
Pavel

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

다음 구조체 및 확장 방법을 추가 한 후

구조체 및 확장 메서드는 Enumerable을 캡슐화합니다. 기능을 선택합니다.

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 를 사용합니다 .
코드 InChaos

3

필자는 Phil Haack이 Razor Templated Delegate ( 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가 아닌 환경에서는 단일 작업 (예 : 람다로 제공 될 수있는 작업)을 수행하는 경우 Razor가 아닌 컨텍스트에서 for / foreach 구문을 완전히 대체하지는 않습니다. .


3

나는 이것이 매우 효율적이라고 생각하지 않지만 작동합니다.

@foreach (var banner in Model.MainBanners) {
    @Model.MainBanners.IndexOf(banner)
}

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); '
Warren LaFrance

1
누군가이 O (n * n)가 무엇인지 설명해 줄 수 있습니까?
Axel

작업이있는 경우, 차적으로 즉 결과의 증가를 계산하는 데 필요한 것을 기본적으로 의미를 @Axel n항목은 다음 작업은 n * n, 또는 n-squared. en.wikipedia.org/wiki/…
Richard Hansell

2

foreach 루프의 현재 반복 값을 얻는 방법이 없다고 생각합니다. 자신을 세는 것이 가장 좋은 방법 인 것 같습니다.

왜 물어보고 싶습니까?

대부분의 likley가 다음 세 가지 중 하나를 수행하는 것 같습니다.

1) 컬렉션에서 객체 가져 오기, 그러나이 경우 이미 가지고 있습니다.

2) 나중에 사후 처리를 위해 객체를 계산합니다 ... 컬렉션에는 사용할 수있는 Count 속성이 있습니다.

3) 루프의 순서에 따라 객체의 속성을 설정합니다 ...하지만 컬렉션에 객체를 추가했을 때 쉽게 설정할 수 있습니다.


4) 여러 번 쳤다는 경우는 첫 번째 또는 마지막 패스에서 수행해야하는 것과는 다릅니다. 예를 들어 인쇄 할 개체 목록과 항목 사이에 쉼표가 필요하지만 마지막 항목 이후에는 그렇지 않습니다.
Loren Pechtel

2

컬렉션이 어떤 메소드를 통해 객체의 인덱스를 반환하지 않는 한 유일한 방법은 예제와 같이 카운터를 사용하는 것입니다.

그러나 인덱스 작업시 문제에 대한 합리적인 대답은 for 루프를 사용하는 것입니다. 다른 어떤 것도 시간과 공간의 복잡성을 언급하지 않고 코드 복잡성을 도입합니다.


2

이런 건 어때? myEnumerable이 비어 있으면 myDelimitedString이 널일 수 있습니다.

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) 루프 반복마다 문자열 연결 + =.
enorl76

2

방금이 문제가 있었지만 내 경우의 문제를 생각하면 예상 솔루션과 관련이없는 최상의 솔루션을 얻었습니다.

꽤 일반적인 경우 일 수 있습니다. 기본적으로 하나의 소스 목록에서 읽고 대상 목록에서 객체를 기반으로 객체를 생성하지만 소스 항목이 먼저 유효한지 확인하고 모든 행을 반환해야합니다 오류. 언뜻 보면, Current 속성에서 개체의 열거 자에 인덱스를 가져오고 싶지만, 이러한 요소를 복사 할 때 어쨌든 현재 대상에서 현재 인덱스를 암시 적으로 알고 있습니다. 분명히 대상 객체에 따라 다르지만 나에게 그것은 목록이었고 대부분 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)인지에 따라 이름별로 사전이 표시됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.