foreach 루프의 현재 반복을 나타내는 값을 얻기 위해 C #에서 내가 경험하지 않은 희귀 언어 구조 (최근에 배운 소수, 스택 오버플로에 대한 일부)가 있습니까?
예를 들어, 현재 상황에 따라 이와 같은 작업을 수행합니다.
int i = 0;
foreach (Object o in collection)
{
// ...
i++;
}
답변
는 foreach
구현 컬렉션을 통해 반복입니다 IEnumerable
. GetEnumerator
컬렉션 을 호출하여이 작업 을 수행하면을 반환합니다 Enumerator
.
이 열거 자에는 메소드와 속성이 있습니다.
- MoveNext ()
- 흐름
Current
열거자가 현재있는 개체를 반환하고 다음 개체로 MoveNext
업데이트 Current
합니다.
인덱스 개념은 열거 개념과는 다른 개념이므로 수행 할 수 없습니다.
이 때문에 대부분의 컬렉션은 인덱서와 for 루프 구문을 사용하여 순회 할 수 있습니다.
이 상황에서 로컬 변수로 색인을 추적하는 것과 비교하여 for 루프를 사용하는 것이 좋습니다.
답변
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 }
새로운 창조하고 익명의 개체를 .
다음을 사용하여 힙 할당을 피할 수 있습니다. 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>
답변
마지막으로 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));
답변
다음과 같이 할 수 있습니다 :
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));
}
}
답변
for
대부분의 경우 루프가 더 나은 선택 이라는 의견에 동의하지 않습니다 .
foreach
유용한 구문이며 for
모든 상황에서 루프로 대체 할 수 없습니다 .
예를 들어 DataReader가 있고 foreach
이를 사용하여 모든 레코드를 반복하는 경우 자동으로 Dispose를 호출합니다. 메소드를 하고 리더를 닫습니다 (연결을 자동으로 닫을 수 있음). 따라서 리더를 닫는 것을 잊어 버린 경우에도 연결 누출을 방지하므로 더 안전합니다.
(항상 독자를 닫는 것이 좋지만 컴파일러가 그렇지 않은 경우 컴파일러가이를 포착하지는 않습니다. 모든 독자를 닫았다 고 보장 할 수는 없지만 연결을 통해 누수가 발생하지 않도록 할 수는 있습니다) foreach를 사용하는 습관.)
Dispose
유용한 메소드 의 내재적 호출에 대한 다른 예가있을 수 있습니다 .
답변
리터럴 답변-경고, 성능은 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);
}
답변
LINQ, C # 7 및 System.ValueTuple
NuGet 패키지를 사용하여 다음을 수행 할 수 있습니다.
foreach (var (value, index) in collection.Select((v, i)=>(v, i))) {
Console.WriteLine(value + " is at index " + index);
}
일반 foreach
구문을 사용하고 개체의 구성원이 아닌 값과 인덱스에 직접 액세스 할 수 있으며 두 필드를 모두 루프 범위에만 유지합니다. 이러한 이유로 C # 7 및을 사용할 수 있다면 이것이 최선의 해결책이라고 생각합니다 System.ValueTuple
.