[C#] IEnumerable에 ForEach 확장 방법이없는 이유는 무엇입니까?

실종에 대해 묻는 또 다른 질문에서 영감을 얻었습니다. Zip 기능 .

수업에 ForEach확장 방법 이없는 이유는 무엇 Enumerable입니까? 아니면 어디서? ForEach메소드 를 얻는 유일한 클래스 는 List<>입니다. 누락 된 이유 (성능)가 있습니까?



답변

대부분의 작업을 수행하는 언어에는 이미 foreach 문이 포함되어 있습니다.

나는 다음을 보는 것을 싫어한다.

list.ForEach( item =>
{
    item.DoSomething();
} );

대신에:

foreach(Item item in list)
{
     item.DoSomething();
}

후자는 입력하는 데 약간 더 길지만 대부분의 상황에서 더 명확하고 읽기 쉽습니다 .

그러나 그 문제에 대한 입장을 바꿨 음을 인정해야합니다. ForEach () 확장 메소드는 실제로 어떤 상황에서는 유용 할 것입니다.

명령문과 메소드의 주요 차이점은 다음과 같습니다.

  • 유형 검사 : foreach는 런타임에 수행되고 ForEach ()는 컴파일 시간에 수행됩니다 (Big Plus!).
  • 델리게이트를 호출하는 구문은 실제로 훨씬 간단합니다. objects.ForEach (DoSomething);
  • ForEach ()는 연결될 수 있습니다 : 그러한 기능의 악 / 유용성은 토론에 열려 있습니다.

이것들은 모두 많은 사람들에 의해 만들어졌으며 사람들이 왜 기능이 빠져 있는지 볼 수 있습니다. Microsoft가 다음 프레임 워크 반복에서 표준 ForEach 메서드를 추가해도 상관 없습니다.


답변

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를 갖는 것은 부작용을 피하기보다는 구체적으로 초대합니다. -키이스 파머 (파트너)

기본적으로 확장 방법을 기능적으로 “순결”하게 유지하기로 결정했습니다. 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()어쨌든 네이티브 가 전체 컬렉션을 실현해야하기 때문에 일반적으로 추가 메모리가 필요하지 않습니다 .

단점

작업 순서는 이상적이지 않습니다. 차라리 하나의 요소를 깨닫고 행동하고 반복합니다. 이 코드는 모든 요소를 ​​먼저 인식 한 다음 각 요소를 순서대로 작동합니다.

목록을 실현하면 예외가 발생하더라도 단일 요소에 대해 조치를 취하지 않아도됩니다.

열거 형이 자연수와 같이 무한하다면 운이 나쁘다.


답변

나는 항상 나 자신을 궁금해했습니다. 그래서 나는 항상 이것을 가지고 다닙니다.

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

작은 확장 방법이 좋습니다.