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.