LINQ에서 다음과 동등한 작업을 수행하고 싶지만 방법을 알 수 없습니다.
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
실제 구문은 무엇입니까?
답변
IEnumerable
;에 대한 ForEach 확장이 없습니다 . 에 대해서만 List<T>
. 그래서 당신은 할 수 있습니다
items.ToList().ForEach(i => i.DoStuff());
또는 고유 한 ForEach 확장 방법을 작성하십시오.
public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach(T item in enumeration)
{
action(item);
}
}
답변
Fredrik이 수정을 제공했지만 이것이 왜 프레임 워크에 없는지 고려해 볼 가치가 있습니다. LINQ 쿼리 연산자는 부작용없이 세상을 바라 보는 합리적인 기능적 방식에 적합해야한다는 생각입니다. A – foreach는 분명히 반대 정확히 순수 부작용은 기반 구조.
그것은 이것이 나쁜 짓이라고 말하는 것은 아닙니다. 결정의 철학적 이유에 대해서만 생각하면됩니다.
답변
2012 년 7 월 17 일 업데이트 : 분명히 C # 5.0부터 foreach
아래 설명 된 동작 이 변경되었으며 ” 중첩 된 람다 식에서 반복 변수를 사용해 foreach
도 더 이상 예기치 않은 결과 가 발생하지 않습니다 . “이 답변은 C # ≥ 5.0에는 적용되지 않습니다 . .
@John Skeet 및 foreach 키워드를 선호하는 모든 사람.
5.0 이전의 C #에서 “foreach”의 문제점 은 다른 언어에서 동등한 “이해”가 작동하는 방식과 작동 할 것으로 예상되는 방식과 일치하지 않는다는 것입니다 (다른 사람들이 가독성에 관한 의견). ” 유해한 것으로 간주되는 루프 변수 닫기 “뿐만 아니라 ” 수정 된 클로저에 액세스 “와 관련된 모든 질문을 참조하십시오 . 이것은 “foreach”가 C #에서 구현되는 방식 때문에 “유해”입니다.
@Fredrik Kalseth의 답변과 기능적으로 동등한 확장 방법을 사용하여 다음 예를 살펴보십시오.
public static class Enumerables
{
public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
{
foreach (T item in @this)
{
action(item);
}
}
}
지나치게 고안된 사례에 대한 사과. 나는 Observable 만 사용하고 있습니다. 이와 같은 일을하기 위해 완전히 가져 오지 않았기 때문입니다. 분명히이 관찰 가능 객체를 만드는 더 좋은 방법이 있습니다. 저는 요점을 보여 주려고합니다. 일반적으로 Observable에 가입 된 코드는 비동기 적으로 잠재적으로 다른 스레드에서 실행됩니다. “foreach”를 사용하면 매우 이상하고 결정적이지 않은 결과가 발생할 수 있습니다.
“ForEach”확장 방법을 사용한 다음 테스트는 통과합니다.
[Test]
public void ForEachExtensionWin()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
values.ForEach(value =>
source.OnNext(() => value));
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Win
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}
다음은 오류와 함께 실패합니다.
예상 : <0, 1, 2, 3, 4, 5, 6, 7, 8, 9>와 같음 : <9, 9, 9, 9, 9, 9, 9, 9, 9, 9>
[Test]
public void ForEachKeywordFail()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
foreach (var value in values)
{
//If you have resharper, notice the warning
source.OnNext(() => value);
}
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Fail
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}
답변
당신은 사용할 수 있습니다 FirstOrDefault()
사용할 수 확장, IEnumerable<T>
. false
술어에서 리턴 하면 각 요소에 대해 실행되지만 실제로 일치하는 것을 찾지는 않습니다. ToList()
오버 헤드 를 피할 수 있습니다.
IEnumerable<Item> items = GetItems();
items.FirstOrDefault(i => { i.DoStuff(); return false; });
답변
Fredrik의 방법을 사용하여 반환 유형을 수정했습니다.
이런 방법으로 다른 LINQ 메서드와 같이 지연된 실행을 지원 합니다.
편집 : 명확하지 않은 경우이 메소드의 사용은 ToList () 또는 메소드가 완전한 열거 가능하게 작동하도록 강제하는 다른 방법으로 끝나야합니다 . 그렇지 않으면 작업이 수행되지 않습니다!
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach (T item in enumeration)
{
action(item);
yield return item;
}
}
그리고 그것을 볼 수있는 테스트가 있습니다 :
[Test]
public void TestDefferedExecutionOfIEnumerableForEach()
{
IEnumerable<char> enumerable = new[] {'a', 'b', 'c'};
var sb = new StringBuilder();
enumerable
.ForEach(c => sb.Append("1"))
.ForEach(c => sb.Append("2"))
.ToList();
Assert.That(sb.ToString(), Is.EqualTo("121212"));
}
결국 ToList () 를 제거하면 StringBuilder에 빈 문자열이 포함되어 있으므로 테스트가 실패하는 것을 볼 수 있습니다. ForEach가 강제로 열거하는 방법이 없기 때문입니다.
답변
IEnumerable에서 부작용을 피하십시오
LINQ에서 다음과 동등한 작업을 수행하고 싶지만 방법을 알 수 없습니다.
다른 사람들이 여기 와 해외에서 지적했듯이 LINQ와 IEnumerable
방법은 부작용이 없을 것으로 예상됩니다.
IEnumerable의 각 항목에 “뭔가”를 정말로 하시겠습니까? 그렇다면 foreach
최선의 선택입니다. 부작용이 발생했을 때 사람들은 놀라지 않습니다.
foreach (var i in items) i.DoStuff();
부작용을 원하지 않겠어요
그러나 내 경험상 부작용은 일반적으로 필요하지 않습니다. Jon Skeet, Eric Lippert 또는 Marc Gravell이 제공하는 StackOverflow.com 답변과 함께 간단한 LINQ 쿼리가 발견되기를 기다리는 경우가 종종 있습니다.
몇 가지 예
실제로 일부 값을 집계 (누적)하는 경우 Aggregate
확장 방법을 고려해야합니다 .
items.Aggregate(initial, (acc, x) => ComputeAccumulatedValue(acc, x));
아마도 IEnumerable
기존 값에서 새로운 것을 만들고 싶을 것 입니다.
items.Select(x => Transform(x));
또는 룩업 테이블을 만들고 싶을 수도 있습니다.
items.ToLookup(x, x => GetTheKey(x))
가능성 목록 (전적으로 의도 된 것은 아님)이 계속 이어지고 있습니다.
답변
열거 형 롤로 작동하려면 각 항목을 생성 해야합니다 .
public static class EnumerableExtensions
{
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach (var item in enumeration)
{
action(item);
yield return item;
}
}
}