IEnumerable의 foreach에 해당하는 LINQ
LINQ에서 다음과 같은 작업을 수행하고 싶지만 방법을 알 수 없습니다.
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
실제 구문은 무엇입니까?
에 대한 ForEach 확장은 없습니다 IEnumerable
. 에만 해당됩니다 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 쿼리 연산자가 부작용이 없어야하며 합리적으로 기능적으로 세계를 바라 보는 방식에 적합해야한다고 생각합니다. 분명히 ForEach는 순전히 부작용 기반 구조입니다.
이것이 나쁜 일이라고 말하는 것이 아니라 결정 뒤에있는 철학적 이유에 대해 생각하는 것입니다.
업데이트 2012년 7월 17일 : 분명히 C # 5.0으로의 동작을 foreach
아래에 설명이 변경되었습니다와 " a의 사용 foreach
중첩 된 람다 식에 반복 변수는 더 이상 예기치 않은 결과를 얻을 수 없습니다. "이 대답은 C #을 ≥ 5.0에 적용되지 않습니다 .
@John Skeet 및 foreach 키워드를 선호하는 모든 사람.
5.0 이전의 C #에서 "foreach"의 문제점 은 동등한 "for comprehension"이 다른 언어에서 작동하는 방식과 내가 그것이 작동 할 것으로 기대하는 방식과 일치하지 않는다는 것입니다 (다른 사람들이 자신의 가독성에 대한 의견). " 수정 된 클로저에 대한 액세스 "및 " 유해한 것으로 간주되는 루프 변수에 대한 클로징 "에 관한 모든 질문을 참조하십시오 . 이것은 "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;
}
}
}
LINQ에 대한 대화 형 확장 에 대한 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));
PLINQ (.Net 4.0부터 사용 가능)에 따르면 다음을 수행 할 수 있습니다.
IEnumerable<T>.AsParallel().ForAll()
IEnumerable에서 병렬 foreach 루프를 수행합니다.
ForEach의 목적은 부작용을 일으키는 것입니다. IEnumerable은 집합의 지연 열거를위한 것입니다.
이 개념적 차이는 고려할 때 매우 눈에.니다.
SomeEnumerable.ForEach(item=>DataStore.Synchronize(item));
이것은 "count"또는 "ToList ()"또는 그 위에 뭔가를 할 때까지 실행되지 않습니다. 분명히 표현 된 것이 아닙니다.
반복 체인을 설정하고 해당 소스 및 조건에 따라 콘텐츠를 정의하려면 IEnumerable 확장을 사용해야합니다. Expression Tree는 강력하고 효율적이지만 그 특성을 이해하는 법을 배워야합니다. 그리고 게으른 평가를 무시하는 문자 몇 개를 절약하기 위해 프로그래밍하는 것만이 아닙니다.
많은 사람들이 그것을 언급했지만 나는 그것을 기록해야했습니다. 이것은 가장 명확하고 / 가장 읽기 쉬운가요?
IEnumerable<Item> items = GetItems();
foreach (var item in items) item.DoStuff();
짧고 간단합니다.
많은 답변이 이미 지적했듯이 이러한 확장 방법을 직접 쉽게 추가 할 수 있습니다. 그러나 그렇게하고 싶지 않다면 BCL에서 이와 같은 것을 알지 못하더라도 System
이미 Reactive Extension에 대한 참조가있는 경우 네임 스페이스에 옵션 이 있습니다 (그렇지 않은 경우 , 있어야합니다) :
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));
물론 이것은 완전히 새로운 쓰레드 웜 캔을 열어줍니다.
ps (글꼴에 대해 죄송합니다. 시스템이 결정한 것입니다)
이 "기능적 접근"추상화는 큰 시간을 흘립니다. 언어 수준의 어떤 것도 부작용을 예방하지 않습니다. 컨테이너의 모든 요소에 대해 람다 / 대리자를 호출 할 수있는 한 "ForEach"동작을 얻게됩니다.
예를 들어 srcDictionary를 destDictionary로 병합하는 한 가지 방법 (키가 이미 존재하는 경우-덮어 쓰기)
이것은 해킹이며 프로덕션 코드에서 사용해서는 안됩니다.
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는 또한 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;
}
}
The Lazy version NEEDs to be materialized, ToList() for example, otherwise, nothing happens. see below great comments from ToolmakerSteve.
IQueryable<Product> query = Products.Where(...);
query.ForEachLazy(t => t.Price = t.Price + 1.00)
.ToList(); //without this line, below SubmitChanges() does nothing.
SubmitChanges();
I keep both ForEach() and ForEachLazy() in my library.
I respectually disagree with the notion that link extension methods should be side-effect free (not only because they aren't, any delegate can perform side effects).
Consider the following:
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);
}
}
What the example shows is really just a kind of late-binding that allows one invoke one of many possible actions having side-effects on a sequence of elements, without having to write a big switch construct to decode the value that defines the action and translate it into its corresponding method.
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;
}
참고URL : https://stackoverflow.com/questions/200574/linq-equivalent-of-foreach-for-ienumerablet
'code' 카테고리의 다른 글
평범한 영어로 모나드? (0) | 2020.09.30 |
---|---|
Python 2.X에서 범위와 xrange 함수의 차이점은 무엇입니까? (0) | 2020.09.30 |
Git에서 로컬 분기를 원격 분기로 완전히 바꾸는 방법은 무엇입니까? (0) | 2020.09.30 |
jQuery를 사용하여 Bootstrap 모달 창을 여는 방법은 무엇입니까? (0) | 2020.09.30 |
Git의 특정 개정판에서 단일 파일을 검색하는 방법 (0) | 2020.09.29 |