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


717

LINQ에서 다음과 동등한 작업을 수행하고 싶지만 방법을 알 수 없습니다.

IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());

실제 구문은 무엇입니까?


99
Eric Lippert의 "foreach"와 "ForEach" 를 고려하십시오 . 사용 하지 않거나 공급 하지 않는 것에 대한 정당성을 제공합니다 ForEach().

4
@ pst : 프로젝트 에서이 확장 방법을 맹목적으로 사용했습니다. 그 기사에 감사드립니다.
Robin Maben


2
매우 섹시하지 반면, ToList를 통해 복사본을 만들지 않고 한 줄에이 맞는 -foreach (var i in items) i.Dostuff();
제임스 웨스트 게이트

1
이 링크 중 일부가 죽었습니다! 너무 많이 투표 된 기사는 무엇입니까?
워렌

답변:


863

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

213
ToList ()는 시퀀스의 사본을 작성하므로 성능 및 메모리 문제가 발생할 수 있으므로 ToList ()에주의하십시오.
decasteljau

15
".Foreach ()"가 더 좋은 이유는 몇 가지가 있습니다. 1. PLINQ로 멀티 스레딩 가능. 예외 : 루프에서 모든 예외를 수집하여 한 번에 처리 할 수 ​​있습니다. 그것들은 노이즈 코드입니다.
Dennis C

12
PLINQ는 ForEach가 아닌 ForAll을 사용하므로 ForEach를 사용하지 않습니다.
user7116

9
IENumerable<T>확장 메소드에서를 리턴해야합니다 . 마찬가지로 :public static IEnumerable<T> ForAll<T>(this IEnumerable<T> numeration, Action<T> action) { foreach (T item in enumeration) { action(item); } return enumeration; }
키스 C. 바커

22
첫 번째 경우 개발자는 1) 타이핑을 거의 저장하지 않고 2) 불필요하게 메모리를 할당하여 전체 시퀀스를 목록으로 버리기 전에 목록으로 저장합니다. LINQ 전에 생각
Mark Sowul

364

Fredrik이 수정을 제공했지만 이것이 왜 프레임 워크에 없는지 고려해 볼 가치가 있습니다. LINQ 쿼리 연산자는 부작용없이 세상을 바라 보는 합리적인 기능적 방식에 적합해야한다는 생각입니다. A - foreach는 분명히 반대 정확히 순수 부작용은 기반 구조.

그것은 이것이 나쁜 짓이라고 말하는 것은 아닙니다. 결정의 철학적 이유에 대해서만 생각하면됩니다.


44
그럼에도 불구하고 F #은 Seq.iter를 가지고 있습니다
Stephen Swensen

6
그러나 LINQ 함수가 일반적으로 foreach ()가 제공하지 않는 매우 유용한 것이 있습니다. ) 구성합니다. 그리고 함수형 언어는 부작용이있는 반복을 허용해야합니다. 그렇지 않으면 절대로 하나의 작업을 수행 할 수 없습니다 :) 전형적인 함수형 방법은 원래 열거 형을 변경하지 않고 반환하는 것입니다.
Walt W

3
@ Walt : 전형적인 진정한 기능적 접근 방식이 이와 같이 foreach를 사용 하지 않기 때문에 부분적으로 동의한다고 확신하지 못합니다 .LINQ 와 같은 기능적 접근 방식을 사용하여 새로운 시퀀스를 생성합니다.
Jon Skeet

12
@Stephen Swensen : 예, F #에는 Seq.iter가 있지만 F #에는 불변 데이터 구조가 있습니다. 이것들에서 Seq.iter를 사용할 때, 원래의 Seq는 수정되지 않습니다; 부작용이있는 새로운 Seq가 반환됩니다.
Anders

7
@Anders- Seq.iter부작용이있는 새로운 seq는 물론 아무것도 반환하지 않습니다. 실제로 부작용에 관한 것입니다. 의 확장 방법 과 정확히 동일하기 Seq.map때문에 생각할 수도 있습니다 . Seq.iterForEachIEnumerabe<_>
Joel Mueller

39

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

resharper 경고는 무엇을 말합니까?
reggaeguitar

1
@reggaeguitar C # 5.0이 출시되기 전 7 ~ 8 년 동안 C #을 사용하지 않았습니다. 여기에서 설명한 동작이 변경되었습니다. 당시는이 경고했다 stackoverflow.com/questions/1688465/...을 . 그래도 여전히 경고한다고 의심합니다.
drstevens 2016 년

34

당신은 사용할 수 있습니다 FirstOrDefault()사용할 수 확장, IEnumerable<T>. false술어에서 리턴 하면 각 요소에 대해 실행되지만 실제로 일치하는 것을 찾지는 않습니다. ToList()오버 헤드 를 피할 수 있습니다.

IEnumerable<Item> items = GetItems();
items.FirstOrDefault(i => { i.DoStuff(); return false; });

11
나도 동의합니다. 영리한 작은 해킹 일 수 있지만 처음에는이 코드가 표준 foreach 루프와 비교하여 현재 수행중인 작업이 명확하지 않습니다.
Connell

26
나는 동안 때문에 downvote했다 기술적으로 대신하기 때문에 정확한, 당신의 미래의 자신과 모든 후속 영원히 당신을 미워 foreach(Item i in GetItems()) { i.DoStuff();}하면 더 많은 문자를했다 그리고 그것은 매우 혼란했다
마크 Sowul

14
좋아, 그러나 더 읽기 쉬운 해킹 :items.All(i => { i.DoStuff(); return true; }
nawfal

5
나는 이것이 꽤 오래된 게시물이라는 것을 알고 있지만 그것을 공감해야했습니다. 나는 그것이 영리한 속임수에 동의하지만 유지 관리 할 수는 없습니다. 언뜻보기에는 목록의 첫 번째 항목으로 만 무언가를하는 것처럼 보입니다. 이것은 미래에 코더가 어떻게 작동하고 무엇을해야하는지 오해함으로써 더 많은 버그를 야기 할 수 있습니다.
Lee

4
이것은 부작용 프로그래밍이며 IMHO는 권장하지 않습니다.
Anish

21

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가 강제로 열거하는 방법이 없기 때문입니다.


의 대체 구현은 ForEach재미있다, 그러나 그것의 동작과 일치하지 않는 List.ForEach것입니다 서명있는, public void ForEach(Action<T> action). 또한 Observable.ForEach확장 의 동작과 IObservable<T>의 서명이 일치하지 않습니다 public static void ForEach<TSource>(this IObservable<TSource> source, Action<TSource> onNext). ForEachScala 컬렉션에 해당하는 FWIW, 게으른 컬렉션에도 C #에서 void와 동일한 반환 유형이 있습니다.
drstevens

이것이 가장 좋은 대답입니다. 지연된 실행 + 유창한 API.
Alexander Danilov

3
이것이 바로 호출하는 것과 동일하게 작동 Select으로 ToList. 의 목적은 ForEach전화 할 필요가 없습니다 ToList. 즉시 실행해야합니다.
t3chb0t

3
실행이 지연된이 "ForEach"를 사용하면 즉시 실행되는 것의 이점은 무엇입니까? 연기 (ForEach 사용 방식에 불편 함)는 조치에 부작용 ([Linq 운영자에 대해 발생하는 일반적인 불만])이있는 경우 ForEach가 유용한 작업 만 수행한다는 사실을 개선하지 않습니다. 그렇다면 이 변화 어떻게 도움이됩니까? 또한 추가 된 "ToList ()"를 강제 실행하여 유용한 목적을 제공하지 않습니다. 사고가 기다리고 있습니다. "ForEach"의 요점은 부작용입니다. 반환 된 열거 형을 원하면 SELECT가 더 적합합니다.
ToolmakerSteve

20

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

가능성 목록 (전적으로 의도 된 것은 아님)이 계속 이어지고 있습니다.


7
아, Select는 Map이고 Aggregate는 Reduce입니다.
Darrel Lee

17

열거 형 롤로 작동하려면 각 항목을 생성 해야합니다 .

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

이것은 아이템을 반품 할 때 실제로 ForEach가 아니며 Tap과 비슷합니다.
EluciusFTW

10

LINQ에 대한 Interactive Extensions의 Microsoft의 실험 릴리스가 있습니다 ( NuGet 에서도 자세한 링크는 RxTeams의 프로파일 참조 ). 채널 9 비디오는 잘 설명합니다.

문서는 XML 형식으로 만 제공됩니다. 이 문서를 더 읽기 쉬운 형식으로 만들 수 있도록 Sandcastle에서 실행했습니다 . 문서 보관 파일의 압축을 풀고 index.html을 찾으십시오 .

다른 많은 장점들 중에서도 예상되는 ForEach 구현을 제공합니다. 다음과 같은 코드를 작성할 수 있습니다.

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 };

numbers.ForEach(x => Console.WriteLine(x*x));

2
Interactive Extensions에 대한 작업 링크 : microsoft.com/download/en/details.aspx?id=27203
Maciek Świszczowski

8

PLINQ (.Net 4.0부터 사용 가능)에 따르면

IEnumerable<T>.AsParallel().ForAll() 

IEnumerable에서 병렬 foreach 루프를 수행합니다.


작업이 스레드로부터 안전하다면 모든 것이 좋습니다. 그러나 스레드로부터 안전하지 않은 조치를 수행하면 어떻게됩니까? 그것은 꽤 신체 상해를 일으킬 수 있습니다 ...
shirbr510 03.

2
진실. 스레드 안전하지 않으며 일부 스레드 풀에서 다른 스레드가 사용됩니다 ... 그리고 대답을 수정해야합니다. 지금 시도 할 때 ForEach ()가 없습니다. 이것에 대해 숙고 할 때 ForEach가 포함 된 Extension이 포함 된 코드 여야합니다.
Wolf5

6

ForEach의 목적은 부작용을 일으키는 것입니다. IEnumerable은 집합의 지연 열거 형입니다.

이 개념상의 차이는 고려할 때 매우 분명합니다.

SomeEnumerable.ForEach(item=>DataStore.Synchronize(item));

"count"또는 "ToList ()"등을 수행 할 때까지 실행되지 않습니다. 그것은 분명히 표현 된 것이 아닙니다.

IEnumerable 확장명을 사용하여 반복 체인을 설정하고 해당 소스 및 조건에 따라 컨텐츠를 정의해야합니다. Expression Tree는 강력하고 효율적이지만 특성을 이해하는 법을 배워야합니다. 그리고 게으른 평가를 무시하는 몇 문자를 저장하기 위해 주변 프로그래밍을위한 것이 아닙니다.


5

많은 사람들이 그것을 언급했지만, 나는 그것을 써야했습니다. 가장 명확하고 읽기 쉬운가요?

IEnumerable<Item> items = GetItems();
foreach (var item in items) item.DoStuff();

짧고 간단합니다.


당신은 foreach 문에서 직접 전체 첫 번째 줄과 사용 GetItems을 삭제할 수 있습니다
tymtam

3
물론이야. 명확성을 위해 다음과 같이 작성되었습니다. 어떤 GetItems()메소드가 리턴 하는지 명확하게 합니다.
Nenad

4
이제 내 의견을 읽을 때 당신이 모르는 것을 말하고있는 것처럼 보입니다. 나는 그것을 의미하지 않았다. 내가 의미하는 foreach (var item in GetItems()) item.DoStuff();것은 그저 아름다움 일뿐입니다.
tymtam

4

이제 우리는 옵션이 있습니다 ...

        ParallelOptions parallelOptions = new ParallelOptions();
        parallelOptions.MaxDegreeOfParallelism = 4;
#if DEBUG
        parallelOptions.MaxDegreeOfParallelism = 1;
#endif
        Parallel.ForEach(bookIdList, parallelOptions, bookID => UpdateStockCount(bookID));

물론, 이것은 완전히 새로운 벌레 벌레 캔을 열어줍니다.

ps (글꼴에 대해 죄송합니다. 시스템이 결정한 것입니다)


4

많은 답변이 이미 지적했듯이 이러한 확장 방법을 쉽게 추가 할 수 있습니다. 당신은 내가 BCL에서 이런 건 잘 모르는 것 같아요 있지만, 그렇게하지 않을 경우,이 여전히에서 옵션의 System이미에 대한 참조가있는 경우, 네임 스페이스 반응성 확장 (그리고 당신이 아닌 경우 , 당신은해야합니다) :

using System.Reactive.Linq;

items.ToObservable().Subscribe(i => i.DoStuff());

메소드 이름은 약간 다르지만 최종 결과는 정확히 당신이 찾고있는 것입니다.


해당 패키지처럼 보인다는 이제 사용되지 않습니다
reggaeguitar

3

ForEach는 또한 Chained 일 수 있습니다 . 작업 후 말뚝에 다시 넣으십시오. 유창하게 남아


Employees.ForEach(e=>e.Act_A)
         .ForEach(e=>e.Act_B)
         .ForEach(e=>e.Act_C);

Orders  //just for demo
    .ForEach(o=> o.EmailBuyer() )
    .ForEach(o=> o.ProcessBilling() )
    .ForEach(o=> o.ProcessShipping());


//conditional
Employees
    .ForEach(e=> {  if(e.Salary<1000) e.Raise(0.10);})
    .ForEach(e=> {  if(e.Age   >70  ) e.Retire();});

열망 구현의 버전.

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enu, Action<T> action)
{
    foreach (T item in enu) action(item);
    return enu; // make action Chainable/Fluent
}

편집 : 게으른 버전처럼, 수율 반환을 사용하고 .

public static IEnumerable<T> ForEachLazy<T>(this IEnumerable<T> enu, Action<T> action)
{
    foreach (var item in enu)
    {
        action(item);
        yield return item;
    }
}

Lazy 버전은 구체화 되어야합니다 (예 : ToList ()). 그렇지 않으면 아무 일도 일어나지 않습니다. 아래의 ToolmakerSteve 의견을 참조하십시오.

IQueryable<Product> query = Products.Where(...);
query.ForEachLazy(t => t.Price = t.Price + 1.00)
    .ToList(); //without this line, below SubmitChanges() does nothing.
SubmitChanges();

라이브러리에 ForEach () 및 ForEachLazy ()를 모두 유지합니다.


2
이것을 테스트 했습니까? 이 방법이 직관적으로 대조적으로 작동하는 몇 가지 컨텍스트가 있습니다. 더 나은 것입니다foreach(T item in enu) {action(item); yield return item;}
Taemyr

2
이것은 여러 열거가 발생합니다
regisbsb

흥미 롭군 위의 작업을 수행하려고 할 때 숙고하는 것이 아니라 3 가지 동작 순서로 수행하십시오 foreach (T item in enu) { action1(item); action2(item); action3(item); }. 하나의 명시 적 루프. action2를 시작하기 전에 모든 action1 을 수행하는 것이 중요한지 추측합니다 .
ToolmakerSteve

@ Rm558- "수율 반환"을 사용한 접근 방식. Pro : 원래 시퀀스를 한 번만 열거하므로 더 넓은 범위의 열거에서 올바르게 작동합니다. 단점 : 마지막에 ".ToArray ()"를 잊어 버리면 아무 일도 일어나지 않습니다. 실패는 명백하지만 오랫동안 간과되지는 않았으므로 큰 비용은 들지 않았습니다. 나에게 "깨끗한 느낌"은 없지만,이 길을 따라가는 사람에게는 올바른 절충안입니다 (ForEach 운영자).
ToolmakerSteve

1
이 코드는 트랜잭션 관점에서 안전하지 않습니다. 실패하면 ProcessShipping()어떻게됩니까? 구매자에게 이메일을 보내고 신용 카드를 청구하지만 절대 그에게 물건을 보내지 않습니까? 확실히 문제를 요구합니다.
zmechanic

2

이 "기능적 접근"추상화는 많은 시간을 누출합니다. 언어 수준에서는 부작용을 예방할 수있는 것은 없습니다. 컨테이너의 모든 요소에 대해 람다 / 대리인을 호출 할 수있는 한 "ForEach"동작이 발생합니다.

예를 들어 srcDictionary를 destDictionary로 병합하는 한 가지 방법 (키가 이미 존재하는 경우-덮어 쓰기)

이것은 해킹이므로 프로덕션 코드에서 사용해서는 안됩니다.

var b = srcDictionary.Select(
                             x=>
                                {
                                  destDictionary[x.Key] = x.Value;
                                  return true;
                                }
                             ).Count();

6
고지 사항을 추가해야하는 경우 게시하지 않아야합니다. 그냥 내 의견.
Andrew Grothe

2
도대체이게 어떻게 나아 foreach(var tuple in srcDictionary) { destDictionary[tuple.Key] = tuple.Value; }? 프로덕션 코드에없는 경우 어디에서 왜 사용해야합니까?
Mark Sowul

3
이것은 원칙적으로 가능하다는 것을 보여주기 위해서입니다. 또한 OP가 요청한 작업을 수행하며 프로덕션에 사용해서는 안되며 여전히 호기심이 많으며 교육적 가치가 있음을 분명히 나타 냈습니다.
Zar Shardan

2

Jon Skeet에서 영감을 얻어 다음과 같이 솔루션을 확장했습니다.

확장 방법 :

public static void Execute<TSource, TKey>(this IEnumerable<TSource> source, Action<TKey> applyBehavior, Func<TSource, TKey> keySelector)
{
    foreach (var item in source)
    {
        var target = keySelector(item);
        applyBehavior(target);
    }
}

고객:

var jobs = new List<Job>() 
    { 
        new Job { Id = "XAML Developer" }, 
        new Job { Id = "Assassin" }, 
        new Job { Id = "Narco Trafficker" }
    };

jobs.Execute(ApplyFilter, j => j.Id);

. . .

    public void ApplyFilter(string filterId)
    {
        Debug.WriteLine(filterId);
    }


1

링크 확장 방법에는 부작용이 없어야한다는 개념에 동의하지 않습니다.

다음을 고려하세요:

   public class Element {}

   public Enum ProcessType
   {
      This = 0, That = 1, SomethingElse = 2
   }

   public class Class1
   {
      private Dictionary<ProcessType, Action<Element>> actions = 
         new Dictionary<ProcessType,Action<Element>>();

      public Class1()
      {
         actions.Add( ProcessType.This, DoThis );
         actions.Add( ProcessType.That, DoThat );
         actions.Add( ProcessType.SomethingElse, DoSomethingElse );
      }

      // Element actions:

      // This example defines 3 distict actions
      // that can be applied to individual elements,
      // But for the sake of the argument, make
      // no assumption about how many distict
      // actions there may, and that there could
      // possibly be many more.

      public void DoThis( Element element )
      {
         // Do something to element
      }

      public void DoThat( Element element )
      {
         // Do something to element
      }

      public void DoSomethingElse( Element element )
      {
         // Do something to element
      }

      public void Apply( ProcessType processType, IEnumerable<Element> elements )
      {
         Action<Element> action = null;
         if( ! actions.TryGetValue( processType, out action ) )
            throw new ArgumentException("processType");
         foreach( element in elements ) 
            action(element);
      }
   }

예제에서 보여주는 것은 실제로 일종의 늦게 구속력이 있는데, 이는 동작을 정의하고 변환하는 값을 해독하기 위해 큰 스위치 구성을 작성할 필요없이 일련의 요소에 부작용을 갖는 많은 가능한 동작 중 하나를 호출 할 수있게합니다. 해당하는 방법으로.


9
확장 방법에 부작용이있을 수 있는지 여부와 그 방법에 차이가 있습니다. 당신은 그것이 가질 수 있다고 지적하고 있지만 부작용이 없어야한다고 제안하는 데는 매우 좋은 이유가있을 수 있습니다. 함수형 프로그래밍에서는 모든 함수에 부작용이 없으며, 함수형 프로그래밍 구문을 사용할 때는 해당 함수가 있다고 가정 할 수 있습니다.
Dave Van den Eynde

1

유창하게 유지하려면 다음과 같은 트릭을 사용할 수 있습니다.

GetItems()
    .Select(i => new Action(i.DoStuf)))
    .Aggregate((a, b) => a + b)
    .Invoke();


-1

또 다른 ForEach

public static IList<AddressEntry> MapToDomain(IList<AddressModel> addresses)
{
    var workingAddresses = new List<AddressEntry>();

    addresses.Select(a => a).ToList().ForEach(a => workingAddresses.Add(AddressModelMapper.MapToDomain(a)));

    return workingAddresses;
}

4
메모리 낭비입니다. ToList를 호출하여 목록에 추가합니다. 왜 이것이 주소를 반환하지 않습니까? (a => MapToDomain (a))?
Mark Sowul
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.