7

特定の条件が満たされている間、列挙子を無期限に進めたい foreach ループ内にネストされた while ループがあります。これを行うには、列挙子を IEnumerator< T > (foreach ループ内にある場合はそうでなければなりません) にキャストしてから、キャストされたオブジェクトで MoveNext() を呼び出しますが、変換できないというエラーが表示されます。

参照変換、ボックス化変換、ボックス化解除変換、ラッピング変換、または null 型変換を介して、型 'System.DateTime' を System.Collections.Generic.IEnumerator に変換することはできません。

        foreach (DateTime time in times)
        {
            while (condition)
            {
                // perform action
                // move to next item
                (time as IEnumerator<DateTime>).MoveNext(); // will not let me do this
            }

            // code to execute after while condition is met
         }

foreach ループ内で IEnumerator を手動でインクリメントする最良の方法は何ですか?

編集: while ループの後に、条件が満たされたときに実行したいコードがあることを示すように編集されました。そのため、while 内を手動でインクリメントし、それから抜け出し、続行するのではなく、トップに戻ります。 . これが不可能な場合は、自分のやり方を再設計するのが最善だと思います。

4

8 に答える 8

15

他の回答の多くは、を使用することを推奨してcontinueいます。これは、必要なことを行うのに非常に役立つ場合があります。ただし、列挙子を手動で移動することを示すために、最初に列挙子が必要ですこれは、ループを として記述することを意味しますwhile

using (var enumerator = times.GetEnumerator())
{
    DateTime time;
    while (enumerator.MoveNext()) 
    {
        time = enumerator.Current;
        // pre-condition code
        while (condition)
        {
            if (enumerator.MoveNext())
            {
                time = enumerator.Current;
                // condition code
            }
            else 
            {
                condition = false;
            }
        }
        // post-condition code
    }
}

あなたのコメントから:

IEnumerator インターフェイスを実装していない場合、foreach ループはどのようにそれを進めることができますか?

あなたのループでtimeは、DateTimeです。ループ内で動作するインターフェイスまたはパターンを実装する必要があるのはオブジェクトではありません。times値のシーケンスでDateTimeあり、列挙可能なパターンを実装する必要があります。これは通常、単純にandメソッドを必要とするIEnumerable<T>andIEnumerableインターフェイスを実装することで実現されます。メソッドは、メソッドとorプロパティを定義するandを実装するオブジェクトを返します。しかし、にキャストすることはできません。そのようなものではなく、シーケンスでもないからです。T GetEnumerator()object GetEnumerator()IEnumerator<T>IEnumeratorbool MoveNext()Tobject CurrenttimeIEnumeratortimes

于 2012-02-13T01:16:14.070 に答える
5

for ループ内から列挙子を変更することはできません。言語はこれを許可しません。ループの次の反復に進むには、continue ステートメントを使用する必要があります。

ただし、ループに続行が必要であるとは確信していません。読む。

コードのコンテキストでは、continue が foreach ブロックを参照するようにするために、while を if に変換する必要があります。

foreach (DateTime time in times)       
{    
     if (condition) 
     {
             // perform action
             continue;
     }
     // code to execute if condition is not met    
}

しかし、このように書くと、次の同等のバリアントがさらに単純であることは明らかです

foreach (DateTime time in times)       
{    
     if (condition) 
     {
             // perform action
     }
     else
     {
            // code to execute if condition is not met   
     } 
}

これは、条件が満たされた後に実行するコードとマークされた部分が、条件が false である各アイテムに対して実行されるため、疑似コードと同等です。

このすべてでの私の仮定は、リスト内の各項目に対して条件が評価されるということです。

于 2012-02-13T01:05:00.850 に答える
3

おそらくあなたは使用できますcontinueか?

于 2012-02-13T01:06:03.757 に答える
3

continue ステートメントを使用します。 continue;

于 2012-02-13T01:06:06.563 に答える
1

私が提案しようとしていることは絶対に容認しませんが、元のラッパーを作成してIEnumerable、基になる列挙子をナビゲートするために使用できるアイテムを返すものに変換することができます。最終結果は次のようになります。

public static void Main(string[] args)
{
  IEnumerable<DateTime> times = GetTimes();
  foreach (var step in times.StepWise())
  {
    while (condition)
    { 
      step.MoveNext();
    }
    Console.WriteLine(step.Current);
  }
}

次に、StepWise拡張メソッドを作成する必要があります。

public static class EnumerableExtension
{
    public static IEnumerable<Step<T>> StepWise<T>(this IEnumerable<T> instance)
    {
        using (IEnumerator<T> enumerator = instance.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                yield return new Step<T>(enumerator);
            }
        }
    }

    public struct Step<T>
    {
        private IEnumerator<T> enumerator;

        public Step(IEnumerator<T> enumerator)
        {
            this.enumerator = enumerator;
        }

        public bool MoveNext()
        {
            return enumerator.MoveNext();
        }

        public T Current
        {
            get { return enumerator.Current; }
        }

    }
}
于 2012-02-16T15:13:52.417 に答える
1

これは単なる推測ですが、あなたがやろうとしているのは、日時のリストを取得し、特定の条件を満たすすべての日時を過ぎて移動し、残りのリストに対してアクションを実行することのようです。それがあなたがしようとしているものなら、おそらく System.Linq の SkipWhile() のようなものが必要です。たとえば、次のコードは一連の日時を取り、カットオフ日より前の日時をすべてスキップします。次に、残りの日時を出力します。

var times = new List<DateTime>()
    {
        DateTime.Now.AddDays(1), DateTime.Now.AddDays(2), DateTime.Now.AddDays(3), DateTime.Now.AddDays(4)
    };

var cutoff = DateTime.Now.AddDays(2);

var timesAfterCutoff = times.SkipWhile(datetime => datetime.CompareTo(cutoff) < 1)
    .Select(datetime => datetime);

foreach (var dateTime in timesAfterCutoff)
{
    Console.WriteLine(dateTime);
}

Console.ReadLine();

そういうことですか?

于 2012-02-13T01:19:47.913 に答える
0

まだ言及されていない代替手段の 1 つは、列挙されるデータ要素に加えて、列挙子が自身へのアクセスを許可するラッパー オブジェクトを返すようにすることです。サンプル:

struct ControllableEnumeratorItem<T>
{
  プライベートな ControllableEnumerator の親。
  public T Value {get {return parent.Value;}}
  public bool MoveNext() {return parent.MoveNext();}
  public ControllableEnumeratorItem(ControllableEnumerator newParent)
    {親 = 新しい親;}
}

このアプローチは、列挙中にコレクションを制御された方法で変更できるようにするデータ構造でも使用できます (たとえば、「DeleteCurrentItem」、「AddBeforeCurrentItem」、および「AddAfterCurrentItem」メソッドを含めることによって)。

于 2012-02-16T14:21:33.287 に答える
0

func を反復子として使用し、そのデリゲートで変更している状態を保持して、反復ごとに評価することができます。

public static IEnumerable<T> FunkyIEnumerable<T>(this Func<Tuple<bool, T>> nextOrNot)
    {
        while(true)
        {
           var result = nextOrNot();

            if(result.Item1)
                yield return result.Item2;

            else
                break;

        }

        yield break;

    }

    Func<Tuple<bool, int>> nextNumber = () => 
            Tuple.Create(SomeRemoteService.CanIContinueToSendNumbers(), 1);

    foreach(var justGonnaBeOne in nextNumber.FunkyIEnumerable())
            Console.Writeline(justGonnaBeOne.ToString());
于 2012-02-13T01:18:13.123 に答える