문제

목록 수업이 있고 무시하고 싶습니다. GetEnumerator() 내 자신의 열거 자 수업을 반환합니다. 이 열거 자 클래스에는 열거자가 사용될 때 업데이트 될 두 가지 추가 속성이 있습니다.

단순성 (정확한 비즈니스 사례가 아님)을 위해, 그 속성이 CurrentIndex 그리고 RunningTotal.

Foreach 루프 내에서 이러한 속성을 수동으로 관리 할 수는 있지만 재사용을 위해이 기능을 캡슐화하고 열거자가 올바른 지점 인 것 같습니다.

문제 : 각각 모든 열거 자 사업을 숨기면 Foreach 명세서 내에서 현재 열거 자에 액세스하여 속성을 검색 할 수있는 방법이 있습니까? 아니면 내가 잊혀지고, 불쾌한 오래된 길을 사용하고, 열거자를 직접 조작해야합니까?

도움이 되었습니까?

해결책

엄밀히 말하면, 나는 당신이 말하는 것을 정확하게하고 싶다면, 그렇습니다. 그렇습니다. GetEnumerator를 호출하고 열거자를 직접 제어해야합니다.

비즈니스 요구 사항에 대해 너무 많이 알지 못하면 다음과 같은 반복 기능을 활용할 수 있습니다.

    public static IEnumerable<decimal> IgnoreSmallValues(List<decimal> list)
    {
        decimal runningTotal = 0M;
        foreach (decimal value in list)
        {
            // if the value is less than 1% of the running total, then ignore it
            if (runningTotal == 0M || value >= 0.01M * runningTotal)
            {
                runningTotal += value;
                yield return value;
            }
        }
    }

그런 다음 다음을 수행 할 수 있습니다.

        List<decimal> payments = new List<decimal>() {
            123.45M,
            234.56M,
            .01M,
            345.67M,
            1.23M,
            456.78M
        };

        foreach (decimal largePayment in IgnoreSmallValues(payments))
        {
            // handle the large payments so that I can divert all the small payments to my own bank account.  Mwahaha!
        }

업데이트 :

좋아, 여기에 내가 "낚시 후크"솔루션이라고 불리는 후속 조치가 있습니다. 이제, 이런 식으로 무언가를해야 할 좋은 이유를 생각할 수 없다는 면책 조항을 추가하겠습니다. 그러나 상황이 다를 수 있습니다.

아이디어는 단순히 반복자 함수로 전달하는 "낚시 후크"객체 (참조 유형)를 만듭니다. Ierator 함수는 낚시 후크 객체를 조작하고 외부 코드에서 여전히 참조가 있으므로 진행중인 작업에 대한 가시성이 있습니다.

    public class FishingHook
    {
        public int Index { get; set; }
        public decimal RunningTotal { get; set; }
        public Func<decimal, bool> Criteria { get; set; }
    }

    public static IEnumerable<decimal> FishingHookIteration(IEnumerable<decimal> list, FishingHook hook)
    {
        hook.Index = 0;
        hook.RunningTotal = 0;
        foreach(decimal value in list)
        {
            // the hook object may define a Criteria delegate that
            // determines whether to skip the current value
            if (hook.Criteria == null || hook.Criteria(value))
            {
                hook.RunningTotal += value;
                yield return value;
                hook.Index++;
            }
        }
    }

당신은 다음과 같이 사용합니다.

        List<decimal> payments = new List<decimal>() {
            123.45M,
            .01M,
            345.67M,
            234.56M,
            1.23M,
            456.78M
        };

        FishingHook hook = new FishingHook();

        decimal min = 0;
        hook.Criteria = x => x > min; // exclude any values that are less than/equal to the defined minimum
        foreach (decimal value in FishingHookIteration(payments, hook))
        {
            // update the minimum
            if (value > min) min = value;

            Console.WriteLine("Index: {0}, Value: {1}, Running Total: {2}", hook.Index, value, hook.RunningTotal);
        }
        // Resultint output is:
        //Index: 0, Value: 123.45, Running Total: 123.45
        //Index: 1, Value: 345.67, Running Total: 469.12
        //Index: 2, Value: 456.78, Running Total: 925.90
        // we've skipped the values .01, 234.56, and 1.23

기본적으로, 낚시 쿠크 물체는 반복자가 실행 방법을 제어 할 수 있습니다. 내가 질문에서 얻은 인상은 반복의 내부 작업에 액세스 할 수있는 방법이 필요했기 때문에 반복 중간에있는 동안 반복되는 방식을 조작 할 수 있지만, 그렇지 않은 경우이 솔루션이 가능할 수 있습니다. 필요한 것을 과도하게하십시오.

다른 팁

와 함께 foreach 실제로 열거자를 얻을 수 없습니다. 그러나 열거자를 반환 할 수 있습니다 (yield) 튜플 그 포함 그 데이터; 사실, 당신은 아마도 linq를 사용하여 당신을 위해 그것을 할 수 있습니다 ...

(나는 할 수 없었다 깨끗하게 LINQ를 사용하여 인덱스 가져 오기 - Aggregate, 그렇지만; 그래서 여기에 튜플 접근법이 있습니다)

using System.Collections;
using System.Collections.Generic;
using System;
class MyTuple
{
    public int Value {get;private set;}
    public int Index { get; private set; }
    public int RunningTotal { get; private set; }
    public MyTuple(int value, int index, int runningTotal)
    {
        Value = value; Index = index; RunningTotal = runningTotal;
    }
    static IEnumerable<MyTuple> SomeMethod(IEnumerable<int> data)
    {
        int index = 0, total = 0;
        foreach (int value in data)
        {
            yield return new MyTuple(value, index++,
                total = total + value);
        }
    }
    static void Main()
    {
        int[] data = { 1, 2, 3 };
        foreach (var tuple in SomeMethod(data))
        {
            Console.WriteLine("{0}: {1} ; {2}", tuple.Index,
                tuple.Value, tuple.RunningTotal);
        }
    }
}

요구 사항에 따라보다 기능적인 방식으로 이와 같은 작업을 수행 할 수도 있습니다. 당신이 요구하는 것은 여러 시퀀스를 "지핑"한 다음 한 번에 그것들을 반복 할 수 있습니다. 당신이 준 예제의 세 가지 시퀀스는 다음과 같습니다.

  1. "값"시퀀스
  2. "인덱스"시퀀스
  3. "런닝 총"시퀀스

다음 단계는 이러한 각 시퀀스를 별도로 지정하는 것입니다.

List<decimal> ValueList
var Indexes = Enumerable.Range(0, ValueList.Count)

마지막은 더 재미 있습니다 ... 내가 생각할 수있는 두 가지 방법은 순서를 요약하는 데 사용되는 임시 변수를 갖거나 각 항목의 합을 다시 계산하는 것입니다. 두 번째는 분명히 성능이 훨씬 적습니다. 나는 오히려 임시를 사용합니다.

decimal Sum = 0;
var RunningTotals = ValueList.Select(v => Sum = Sum + v);

마지막 단계는 이것들을 모두 함께 압축하는 것입니다. .NET 4가 있습니다 지퍼 운영자가 내장되었습니다,이 경우에는 다음과 같습니다.

var ZippedSequence = ValueList.Zip(Indexes, (value, index) => new {value, index}).Zip(RunningTotals, (temp, total) => new {temp.value, temp.index, total});

이것은 당신이 더 많은 것을 지퍼하려고하는 것들이 더 시끄럽게됩니다.

마지막 링크에는 zip 함수를 직접 구현하기위한 소스가 있습니다. 정말 간단한 코드입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top