실종에 대해 묻는 또 다른 질문에서 영감을 얻었습니다. Zip
기능 .
수업에 ForEach
확장 방법 이없는 이유는 무엇 Enumerable
입니까? 아니면 어디서? ForEach
메소드 를 얻는 유일한 클래스 는 List<>
입니다. 누락 된 이유 (성능)가 있습니까?
Parallel.ForEach
위해 스와핑 을 시도 Enumerable.ForEach
합니다. C #은 여기서 쉽게 할 수있는 트릭을 놓쳤습니다.
실종에 대해 묻는 또 다른 질문에서 영감을 얻었습니다. Zip
기능 .
수업에 ForEach
확장 방법 이없는 이유는 무엇 Enumerable
입니까? 아니면 어디서? ForEach
메소드 를 얻는 유일한 클래스 는 List<>
입니다. 누락 된 이유 (성능)가 있습니까?
Parallel.ForEach
위해 스와핑 을 시도 Enumerable.ForEach
합니다. C #은 여기서 쉽게 할 수있는 트릭을 놓쳤습니다.
답변:
대부분의 작업을 수행하는 언어에는 이미 foreach 문이 포함되어 있습니다.
나는 다음을 보는 것을 싫어한다.
list.ForEach( item =>
{
item.DoSomething();
} );
대신에:
foreach(Item item in list)
{
item.DoSomething();
}
후자는 입력하는 데 약간 더 길지만 대부분의 상황에서 더 명확하고 읽기 쉽습니다 .
그러나 그 문제에 대한 입장을 바꿨 음을 인정해야합니다. ForEach () 확장 메소드는 실제로 어떤 상황에서는 유용 할 것입니다.
명령문과 메소드의 주요 차이점은 다음과 같습니다.
이것들은 모두 많은 사람들에 의해 만들어졌으며 사람들이 왜 기능이 빠져 있는지 볼 수 있습니다. Microsoft가 다음 프레임 워크 반복에서 표준 ForEach 메서드를 추가해도 상관 없습니다.
foreach
열거 가능 매개 변수가있는 경우 컴파일 타임에 유형 검사입니다. 가상의 ForEach
확장 방법 과 마찬가지로 제네릭이 아닌 컬렉션에 대한 컴파일 시간 유형 검사 만 부족 합니다.
col.Where(...).OrderBy(...).ForEach(...)
. foreach ()에서 중복 로컬 변수 또는 긴 괄호로 화면이 오염되지 않는 훨씬 간단한 구문.
list.ForEach(item => item.DoSomething())
. 그리고 누가 목록이라고합니까? 명백한 사용 사례는 체인의 끝입니다. foreach 문을 사용하면 전체 체인을 뒤에 넣고 in
블록 내에서 수행 해야하는 작업을 수행해야합니다.
ForEach 방법은 LINQ 전에 추가되었습니다. ForEach 확장을 추가하면 확장 메서드 제약으로 인해 List 인스턴스에 대해 호출되지 않습니다. 나는 그것이 추가되지 않은 이유는 기존의 것과 간섭하지 않기 때문이라고 생각합니다.
그러나이 멋진 기능을 실제로 놓치면 자신의 버전을 롤아웃 할 수 있습니다
public static void ForEach<T>(
this IEnumerable<T> source,
Action<T> action)
{
foreach (T element in source)
action(element);
}
이 확장 방법을 작성할 수 있습니다.
// Possibly call this "Do"
IEnumerable<T> Apply<T> (this IEnumerable<T> source, Action<T> action)
{
foreach (var e in source)
{
action(e);
yield return e;
}
}
찬성
체이닝 가능 :
MySequence
.Apply(...)
.Apply(...)
.Apply(...);
단점
반복을 강제로 할 때까지 실제로는 아무것도하지 않습니다. 따라서 호출해서는 안됩니다 .ForEach()
. 당신은 쓸 수 .ToList()
끝, 또는 당신도,이 확장 방법을 쓸 수있다 :
// possibly call this "Realize"
IEnumerable<T> Done<T> (this IEnumerable<T> source)
{
foreach (var e in source)
{
// do nothing
;
}
return source;
}
이는 배송 C # 라이브러리에서 너무 이탈 된 것일 수 있습니다. 확장 방법에 익숙하지 않은 독자는 코드로 무엇을 만들어야할지 모릅니다.
{foreach (var e in source) {action(e);} return source;}
numbers.Select(n => n*2);
Select()
은 각 요소에 함수를 Apply()
적용하고 Action
각 요소에를 적용 하고 원래 요소를 반환하는 함수의 값을 반환 한다고 말합니다 .
Select(e => { action(e); return e; })
여기서 논의 하면 답이됩니다.
실제로, 내가 목격 한 구체적인 논의는 실제로 기능적 순도에 달려 있습니다. 표현에서, 부작용이 없다는 가정이 자주 있습니다. ForEach를 갖는 것은 부작용을 피하기보다는 구체적으로 초대합니다. -키이스 파머 (파트너)
기본적으로 확장 방법을 기능적으로 "순결"하게 유지하기로 결정했습니다. ForEach는 Enumerable 확장 메서드를 사용할 때 부작용이 없었지만 의도는 없었습니다.
foreach
대부분의 경우 내장 구문 을 사용하는 것이 낫다는 데 동의하지만 ForEach <> 확장 에서이 변형을 사용하면 색인을 정기적으로 관리 해야하는 것보다 조금 더 좋습니다 foreach
.
public static int ForEach<T>(this IEnumerable<T> list, Action<int, T> action)
{
if (action == null) throw new ArgumentNullException("action");
var index = 0;
foreach (var elem in list)
action(index++, elem);
return index;
}
예
var people = new[] { "Moe", "Curly", "Larry" };
people.ForEach((i, p) => Console.WriteLine("Person #{0} is {1}", i, p));
당신에게 줄 것입니다 :
Person #0 is Moe
Person #1 is Curly
Person #2 is Larry
한 가지 해결 방법은 작성하는 것 .ToList().ForEach(x => ...)
입니다.
찬성
이해하기 쉬움-독자는 추가 확장 방법이 아니라 C #과 함께 제공되는 내용 만 알면됩니다.
구문 노이즈는 매우 약합니다 (조금 코드 만 추가).
.ForEach()
어쨌든 네이티브 가 전체 컬렉션을 실현해야하기 때문에 일반적으로 추가 메모리가 필요하지 않습니다 .
단점
작업 순서는 이상적이지 않습니다. 차라리 하나의 요소를 깨닫고 행동하고 반복합니다. 이 코드는 모든 요소를 먼저 인식 한 다음 각 요소를 순서대로 작동합니다.
목록을 실현하면 예외가 발생하더라도 단일 요소에 대해 조치를 취하지 않아도됩니다.
열거 형이 자연수와 같이 무한하다면 운이 나쁘다.
Enumerable.ForEach<T>
방법 이 없지만 방법 이 있다고 미리 언급하면 더 명확 해집니다 List<T>.ForEach
. c) "네이티브 .ForEach ()는 어쨌든 전체 컬렉션을 구현해야하기 때문에 일반적으로 추가 메모리가 필요하지 않습니다." -완전하고 말도 안되는 말. 여기 Enumerable.ForEach <T>의 구현은했다면 C 번호를 제공 할 것입니다 :public static void ForEach<T>(this IEnumerable<T> list, Action<T> action) { foreach (T item in list) action(item); }
나는 항상 나 자신을 궁금해했습니다. 그래서 나는 항상 이것을 가지고 다닙니다.
public static void ForEach<T>(this IEnumerable<T> col, Action<T> action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
foreach (var item in col)
{
action(item);
}
}
작은 확장 방법이 좋습니다.
따라서 ForEach 확장 메서드는 LINQ 확장 메서드와 같은 값을 반환하지 않기 때문에 적절하지 않다는 사실에 대해 많은 의견이있었습니다. 이것은 사실적인 진술이지만 완전히 사실이 아닙니다.
LINQ 확장 메소드는 모두 서로 연결될 수 있도록 값을 리턴합니다.
collection.Where(i => i.Name = "hello").Select(i => i.FullName);
그러나 LINQ가 확장 메서드를 사용하여 구현되었다고해서 확장 메서드가 반드시 같은 방식으로 사용되어 값을 반환 . 값을 반환하지 않는 공통 기능을 노출시키기 위해 확장 메소드를 작성하는 것은 완벽하게 유효한 사용법입니다.
ForEach에 대한 구체적인 주장은 확장 메서드에 대한 제약 조건 (즉, 확장 메서드가 동일한 서명으로 상속 된 메서드를 재정의 하지 않을 것임 )에 따라 사용자 지정 확장 메서드가 중요하지 않은 모든 클래스에서 사용 가능한 상황이있을 수 있다는 것입니다 IEnumerable > 목록 제외 >. 확장 메소드 또는 상속 메소드의 호출 여부에 따라 메소드가 다르게 작동하기 시작할 때 혼동을 일으킬 수 있습니다.<T
<T
(체인 가능하지만 게으른 평가)를 사용할 수 있습니다 Select
먼저 작업을 수행 한 다음 신원을 반환 할 수 있습니다 (또는 원하는 경우 다른 것)
IEnumerable<string> people = new List<string>(){"alica", "bob", "john", "pete"};
people.Select(p => { Console.WriteLine(p); return p; });
Count()
(fafa를 열거하는 가장 저렴한 작업) 또는 어쨌든 필요한 다른 작업을 사용하여 평가되는지 확인 해야합니다.
그래도 표준 라이브러리로 가져 오는 것을보고 싶습니다.
static IEnumerable<T> WithLazySideEffect(this IEnumerable<T> src, Action<T> action) {
return src.Select(i => { action(i); return i; } );
}
위의 코드 people.WithLazySideEffect(p => Console.WriteLine(p))
는 foreach와 실질적으로 동일하지만 게으르고 체인 가능합니다.
Select
더 이상해야 할 일을하지 않습니다. 그것은 OP가 요구하는 것에 대한 해결책입니다. 그러나 주제 가독성에 대해서는 선택이 기능을 실행할 것으로 기대하지 않습니다.
Select
해야 할 일을하지 Select
않으면 호출하는 코드가 아니라 의 구현에 문제가 Select
되는데, 이는 무엇에 영향을 미치지 Select
않습니다.
참고 것을 MoreLINQ NuGet가 제공하는 ForEach
당신이 찾고있는 확장 메서드 (뿐만 아니라 Pipe
대리자를 실행하고 그 결과 산출 방법). 보다:
@Coincoin
foreach 확장 메서드의 진정한 힘은 Action<>
불필요한 메서드를 코드에 추가하지 않고도 재사용 할 수 있다는 것입니다. 10 개의 목록이 있고 동일한 논리를 수행하려고하며 해당 기능이 클래스에 적합하지 않으며 재사용되지 않는다고 가정하십시오. 10 개의 for 루프 나 속하지 않는 도우미 인 일반 함수를 사용하는 대신 모든 논리를 한 곳에 유지할 수 있습니다 ( Action<>
따라서 수십 줄이
Action<blah,blah> f = { foo };
List1.ForEach(p => f(p))
List2.ForEach(p => f(p))
기타...
논리는 한 곳에 있으며 당신은 수업을 오염시키지 않았습니다.
f(p)
, { }
속기를 생략하십시오 : for (var p in List1.Concat(List2).Concat(List2)) f(p);
.
대부분의 LINQ 확장 메서드는 결과를 반환합니다. ForEach는 아무것도 반환하지 않으므로이 패턴에 맞지 않습니다.
public IEnumerable<T> Foreach<T>(this IEnumerable<T> items, Action<T> action) { /* error check */ foreach (var item in items) { action(item); yield return item; } }
F # (다음 버전의 .NET에 있음)이있는 경우 다음을 사용할 수 있습니다.
Seq.iter doSomething myIEnumerable
부분적으로 언어 디자이너가 철학적 관점에서 동의하지 않기 때문입니다.
https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
나는 그것에 대해 블로그 게시물을 썼습니다 : http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx
.NET 4.0에서이 방법을 보려면 여기에서 투표 할 수 있습니다. http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093
3.5에서 IEnumerable에 추가 된 모든 확장 메소드는 LINQ 지원을 위해 존재합니다 (System.Linq.Enumerable 클래스에 정의되어 있음). 이 게시물에서는 foreach가 LINQ : Parallel.For와 유사한 기존 LINQ 확장 방법에 속하지 않는 이유를 설명합니다 .
나입니까 아니면 List <T>입니다. 각각 Linq에 의해 거의 사용되지 않습니다. 원래는
foreach(X x in Y)
여기서 Y는 단순히 IEnumerable (Pre 2.0)이어야하고 GetEnumerator ()를 구현해야했습니다. 생성 된 MSIL을 보면 정확히 동일하다는 것을 알 수 있습니다.
IEnumerator<int> enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
int i = enumerator.Current;
Console.WriteLine(i);
}
( MSIL에 대해서는 http://alski.net/post/0a-for-foreach-forFirst-forLast0a-0a-.aspx 참조 )
그런 다음 DotNet2.0에서 Generics와 List가 나왔습니다. Foreach는 항상 Vistor 패턴을 구현 한 것으로 느꼈습니다 (Gamma, Helm, Johnson, Vlissides의 디자인 패턴 참조).
물론 3.5에서는 대신 Lambda를 사용하여 동일한 효과를 얻을 수 있습니다. 예를 들어 http://dotnet-developments.blogs.techtarget.com/2008/09/02/iterators-lambda-and-linq-oh- 나의/
ForEach <T>는 foreach 키워드가 런타임 검사되는 컴파일 시간 유형 검사를 수행한다고 아직 아무도 지적하지 않았습니다.
두 가지 방법 모두 코드에서 사용되는 리팩토링을 수행 한 후에 foreach 문제를 찾기 위해 테스트 실패 / 런타임 실패를 찾아 내야했기 때문에 .ForEach를 선호합니다.
foreach
컴파일 시간을 덜 확인하는 방법을 알지 못합니다 . 물론, 컬렉션이 제네릭이 아닌 IEnumerable 인 경우 루프 변수는가 object
되지만 가상의 ForEach
확장 방법 에서도 마찬가지입니다 .