LINQ에서 다음과 동등한 작업을 수행하고 싶지만 방법을 알 수 없습니다.
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
실제 구문은 무엇입니까?
foreach (var i in items) i.Dostuff();
LINQ에서 다음과 동등한 작업을 수행하고 싶지만 방법을 알 수 없습니다.
IEnumerable<Item> items = GetItems();
items.ForEach(i => i.DoStuff());
실제 구문은 무엇입니까?
foreach (var i in items) 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);
}
}
IENumerable<T>
확장 메소드에서를 리턴해야합니다 . 마찬가지로 :public static IEnumerable<T> ForAll<T>(this IEnumerable<T> numeration, Action<T> action) { foreach (T item in enumeration) { action(item); } return enumeration; }
Fredrik이 수정을 제공했지만 이것이 왜 프레임 워크에 없는지 고려해 볼 가치가 있습니다. LINQ 쿼리 연산자는 부작용없이 세상을 바라 보는 합리적인 기능적 방식에 적합해야한다는 생각입니다. A - foreach는 분명히 반대 정확히 순수 부작용은 기반 구조.
그것은 이것이 나쁜 짓이라고 말하는 것은 아닙니다. 결정의 철학적 이유에 대해서만 생각하면됩니다.
Seq.iter
부작용이있는 새로운 seq는 물론 아무것도 반환하지 않습니다. 실제로 부작용에 관한 것입니다. 의 확장 방법 과 정확히 동일하기 Seq.map
때문에 생각할 수도 있습니다 . Seq.iter
ForEach
IEnumerabe<_>
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; });
foreach(Item i in GetItems()) { i.DoStuff();}
하면 더 많은 문자를했다 그리고 그것은 매우 혼란했다
items.All(i => { i.DoStuff(); return true; }
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)
. ForEach
Scala 컬렉션에 해당하는 FWIW, 게으른 컬렉션에도 C #에서 void와 동일한 반환 유형이 있습니다.
Select
으로 ToList
. 의 목적은 ForEach
전화 할 필요가 없습니다 ToList
. 즉시 실행해야합니다.
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에 대한 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));
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();
짧고 간단합니다.
GetItems()
메소드가 리턴 하는지 명확하게 합니다.
foreach (var item in GetItems()) item.DoStuff();
것은 그저 아름다움 일뿐입니다.
이제 우리는 옵션이 있습니다 ...
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 4;
#if DEBUG
parallelOptions.MaxDegreeOfParallelism = 1;
#endif
Parallel.ForEach(bookIdList, parallelOptions, bookID => UpdateStockCount(bookID));
물론, 이것은 완전히 새로운 벌레 벌레 캔을 열어줍니다.
ps (글꼴에 대해 죄송합니다. 시스템이 결정한 것입니다)
많은 답변이 이미 지적했듯이 이러한 확장 방법을 쉽게 추가 할 수 있습니다. 당신은 내가 BCL에서 이런 건 잘 모르는 것 같아요 있지만, 그렇게하지 않을 경우,이 여전히에서 옵션의 System
이미에 대한 참조가있는 경우, 네임 스페이스 반응성 확장 (그리고 당신이 아닌 경우 , 당신은해야합니다) :
using System.Reactive.Linq;
items.ToObservable().Subscribe(i => i.DoStuff());
메소드 이름은 약간 다르지만 최종 결과는 정확히 당신이 찾고있는 것입니다.
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 ()를 모두 유지합니다.
foreach(T item in enu) {action(item); yield return item;}
foreach (T item in enu) { action1(item); action2(item); action3(item); }
. 하나의 명시 적 루프. action2를 시작하기 전에 모든 action1 을 수행하는 것이 중요한지 추측합니다 .
ProcessShipping()
어떻게됩니까? 구매자에게 이메일을 보내고 신용 카드를 청구하지만 절대 그에게 물건을 보내지 않습니까? 확실히 문제를 요구합니다.
이 "기능적 접근"추상화는 많은 시간을 누출합니다. 언어 수준에서는 부작용을 예방할 수있는 것은 없습니다. 컨테이너의 모든 요소에 대해 람다 / 대리인을 호출 할 수있는 한 "ForEach"동작이 발생합니다.
예를 들어 srcDictionary를 destDictionary로 병합하는 한 가지 방법 (키가 이미 존재하는 경우-덮어 쓰기)
이것은 해킹이므로 프로덕션 코드에서 사용해서는 안됩니다.
var b = srcDictionary.Select(
x=>
{
destDictionary[x.Key] = x.Value;
return true;
}
).Count();
foreach(var tuple in srcDictionary) { destDictionary[tuple.Key] = tuple.Value; }
? 프로덕션 코드에없는 경우 어디에서 왜 사용해야합니까?
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);
}
MoreLinq에는 IEnumerable<T>.ForEach
다른 유용한 확장 기능이 많이 있습니다. 에 대한 종속성을 가져갈 가치는 없지만 아마도 ForEach
유용한 정보가 많이 있습니다.
링크 확장 방법에는 부작용이 없어야한다는 개념에 동의하지 않습니다.
다음을 고려하세요:
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);
}
}
예제에서 보여주는 것은 실제로 일종의 늦게 구속력이 있는데, 이는 동작을 정의하고 변환하는 값을 해독하기 위해 큰 스위치 구성을 작성할 필요없이 일련의 요소에 부작용을 갖는 많은 가능한 동작 중 하나를 호출 할 수있게합니다. 해당하는 방법으로.
또 다른 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;
}
ForEach()
.