문제

LINQ에서 다음과 같은 일을하고 싶지만 방법을 알 수 없습니다.

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

실제 구문은 무엇입니까?

도움이 되었습니까?

해결책

Foreach Extension은 없습니다 IEnumerable; 만 List<T>. 그래서 당신은 할 수 있습니다

items.ToList().ForEach(i => i.DoStuff());

또는 자신의 Foreach Extension 방법을 작성하십시오.

public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
    foreach(T item in enumeration)
    {
        action(item);
    }
}

다른 팁

Fredrik은 수정 사항을 제공했지만 이것이 왜 이것이 프레임 워크에 있지 않은지 고려할 가치가 있습니다. 아이디어는 LINQ 쿼리 연산자가 부작용이 없어야하며, 세계를 바라 보는 합리적으로 기능적인 방법에 맞아야한다고 생각합니다. 분명히 Foreach는 정확히 반대입니다 - a 전혀 부작용 기반 구성.

그것은 이것이 나쁜 일이라고 말하는 것이 아닙니다. 결정의 배후에있는 철학적 이유에 대해 생각합니다.

업데이트 7/17/2012 : C# 5.0의 동작에 따라 foreach 아래에 설명 된 것이 변경되었고 "a의 사용 foreach 중첩 된 람다 표현식의 반복 변수는 더 이상 예기치 않은 결과를 생성하지 않습니다."이 답변은 C# ≥ 5.0에는 적용되지 않습니다.

@John Skeet과 Foreach 키워드를 선호하는 모든 사람.

C#의 "Foreach"의 문제 5.0 이전, 그것은 "이해력에 대한 동등한"이 다른 언어로 어떻게 작동하는지, 그리고 그것이 어떻게 작동 할 것으로 기대하는지 (다른 사람들이 가독성에 관한 의견을 언급했기 때문에 여기에 언급 된 개인 의견)와 일치하지 않는다는 것입니다. "에 관한 모든 질문을보십시오"수정 된 폐쇄에 대한 액세스" 만큼 잘 "루프 변수를 닫는 것은 유해한 것으로 간주됩니다". 이것은"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 만 사용합니다. 분명히이 관찰 가능한 것을 만들 수있는 더 좋은 방법이 있습니다. 나는 요점을 보여 주려고 노력하고 있습니다. 일반적으로 관찰 가능한에 가입 된 코드는 다른 스레드에서 비동기 적으로 그리고 잠재적으로 실행됩니다. "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를 열거해야하기 때문입니다.

부작용을 내 비유질로 유지하십시오

LINQ에서 다음과 같은 일을하고 싶지만 방법을 알 수 없습니다.

다른 사람들이 지적했듯이 여기 그리고 해외 linq IEnumerable 방법은 부작용이 없을 것으로 예상됩니다.

당신은 정말로 ienumerable의 각 항목에 "무언가를"하고 싶습니까? 그 다음에 foreach 최선의 선택입니다. 부작용이 여기에서 일어날 때 사람들은 놀라지 않습니다.

foreach (var i in items) i.DoStuff();

나는 당신이 부작용을 원하지 않을 것입니다

그러나 내 경험상 부작용은 일반적으로 필요하지 않습니다. Jon Skeet, Eric Lippert 또는 Marc Gravell의 답변과 함께 발견되기를 기다리는 간단한 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;
        }
    }
}

Microsoft의 실험 릴리스가 있습니다 LINQ에 대한 대화식 확장 (또한 Nuget에서, 보다 rxteams의 프로필 더 많은 링크의 경우). 그만큼 채널 9 비디오 잘 설명합니다.

문서는 XML 형식으로 만 제공됩니다. 나는 이것을 실행했다 Sandcastle의 문서 더 읽기 쉬운 형식이되도록합니다. 문서 아카이브를 압축하고 찾으십시오 index.html.

다른 많은 제품들 중에서도 예상되는 예상 구현을 제공합니다. 다음과 같이 코드를 쓸 수 있습니다.

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

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

Plinq (.NET 4.0 이후 사용 가능)에 따르면

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

ienumerable에서 평행 한 foreach 루프를 수행합니다.

Foreach의 목적은 부작용을 일으키는 것입니다. ienumerable은 세트의 게으른 열거를위한 것입니다.

이 개념적 차이는 당신이 그것을 고려할 때 상당히 볼 수 있습니다.

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

이것은 "count"또는 "tolist ()"또는 그 위에 무언가를 할 때까지 실행되지 않습니다. 그것은 분명히 표현 된 것이 아닙니다.

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

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

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

짧고 단순 (ST).

수많은 답변에서 이미 지적했듯이 이러한 확장 방법을 직접 추가 할 수 있습니다. 그러나 BCL에서 이와 같은 것을 알지 못하지만 그렇게하고 싶지 않다면 여전히 옵션이 있습니다. System 이미 참조가있는 경우 네임 스페이스 반응성 확장 (그리고 그렇지 않다면, 당신은 가져야합니다) : :

using System.Reactive.Linq;

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

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

이제 우리는 ...

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

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

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

이 "기능적 접근법"추상화는 큰 시간이 유출됩니다. 언어 수준의 어떤 것도 부작용을 방지합니다. 컨테이너의 모든 요소에 대해 Lambda/Delegate에 전화 할 수있는 한 "Foreach"동작을 얻을 수 있습니다.

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

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

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

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

Foreach도 가능합니다 체인, 행동 후에 파일 라인에 다시 넣으십시오. 유창하게 유지됩니다


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

an 열렬한 구현의 버전.

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

게으른 버전 필요합니다 구체화하려면 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 ()를 모두 유지합니다.

나는 링크 확장 메소드가 부작용이 없어야한다는 개념에 각각 동의하지 않습니다 (대의원이 부작용을 수행 할 수 없기 때문에).

다음을 고려하세요:

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

예제가 보여주는 것은 실제로 일련의 요소에 부작용을 갖는 많은 가능한 동작 중 하나를 호출 할 수있는 일종의 늦은 바인딩 일 뿐이며, 동작을 정의하고 번역하는 값을 해독하기 위해 큰 스위치 구성을 작성하지 않고도 일련의 요소에 부작용이 있습니다. 해당 방법으로.

For VB.NET you should use:

listVariable.ForEach(Sub(i) i.Property = "Value")

To stay fluent one can use such a trick:

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

MoreLinq has IEnumerable<T>.ForEach and a ton of other useful extensions. It's probably not worth taking the dependency just for ForEach, but there's a lot of useful stuff in there.

https://www.nuget.org/packages/morelinq/

https://github.com/morelinq/MoreLINQ

Yet another ForEach Example

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;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top