[linq] IEnumerable <T>에 대해 foreach와 동일한 LINQ

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