2012-02-13 13 views
6

foreachループ内に入れ子のwhileループがあり、特定の条件が満たされている間に列挙子を不定期的に進めたいと思っています。これを行うには、列挙子をIEnumerator < T>(これはforeachループ内にある必要があります)にキャストしてからキャストされたオブジェクトでMoveNext()を呼び出しますが、変換できないというエラーが表示されます。foreachループ内の列挙子を手動でインクリメントする

参照変換、ボクシング変換、ボックス化解除変換、折り返し変換、または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をインクリメントするための最良の方法は何ですか?

EDIT: 条件が満たされた後に実行したいwhileループの後にコードがあることを示すために編集しました。なぜなら、私は手作業でインクリメントしたいのですが、私はトップに戻る。これが不可能な場合は、私がどのようにしているかを再設計することが最善の策だと思います。

+0

、一つのことは、あなたが不足している可能性があります: 'time'は、foreachループの進歩は、それが実装されていないことができればどのようIEnumerator' –

+0

'実装されていない 'DateTime'、ありますIEnumeratorインターフェイス? – Despertar

+1

コードを見てください。 'foreach(DateTime time in times)'です。 'times'は列挙されるオブジェクトです。 –

答えて

12

多くは非常によく、あなたはあなたがする必要がある何を助けることが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 
    } 
} 

:foreachループの事前それができ、それはIEnumeratorインターフェイスを実装していない場合、どのよう

ループでは、timeDateTimeです。ループ内で動作するインタフェースやパターンを実装する必要があるオブジェクトではありません。 timesは、DateTimeという値のシーケンスであり、列挙可能なパターンを実装する必要があります。これは、一般的にT GetEnumerator()object GetEnumerator()のメソッドを必要とするIEnumerable<T>IEnumerableインターフェイスを実装することで実現します。メソッドはとIEnumeratorを実装するオブジェクトを返します。これはbool MoveNext()メソッドとTまたはobject Currentプロパティを定義します。しかしtimeIEnumeratorにキャストすることはできません。そのようなものではなく、どちらもtimesのシーケンスではありません。

+0

優れた説明をありがとう!これは、列挙子を手動で増やす方法に関する私の質問に答えて、なぜ私のキャスティングがうまくいかないかを説明します。私はその時間が列挙子であると仮定しましたが、時間は実際には列挙子の現在のプロパティの値を保持する変数です(間違っている場合は修正してください)。これは、IEnumeratorを継承しないカスタムクラスがIEnumerableコンテナ内で動作する理由を説明します。 – Despertar

+1

反復終了時に非終了while(条件)を見てください –

+0

@David、はい、良い点。 –

5

forループ内から列挙子を変更することはできません。言語はこれを許さない。 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 
    } 
} 

これはあなたの擬似コードと等価であることは明らかです条件が偽であるアイテムごとにが実行されます。

これはすべて、条件がリストの各項目について評価されることを前提としています。

+0

このスニペットがジョブを実行できない理由は、whileループを使用して条件を満たすすべてのアイテムをバッチし、 ifループは毎回変化し、全く異なる動作を生成し、列挙子を手動で増分するという元の質問には答えません。 – Despertar

+0

ifでバッチアップし、elseで処理します。ここのコードは、あなたの疑似コードと同じように動作します。かなりシンプルなループを複雑にしています。 –

+0

私はあなたが今何を意味するかを見て、それはうまくいくでしょう。あなたのご意見ありがとうございます。 – Despertar

3

おそらくcontinueを使用できますか?

+2

これは危険ではありません。回答は質問の形で表現する必要はありません。自信を持ってください! –

3

あなたはcontinue文を使用します。他の回答の continue;

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()); 
+0

コードの書式設定を少し修正しました。 –

+0

乾杯。それは恐ろしいものでした... – OskarK

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

これはあなたがやっていることですか?

+0

条件を満たすアイテムをスキップしたくなかったので、しばらくしてから処理されるサブリストにそれらを蓄積したかったのです。しかし、あなたのLINQの参考文献は、私が最初に使ったものであったLINQを別のショットに与えることを奨励しました。そして、これは私の問題をはるかに簡単な方法で(LINQが通常そうするように)解決しました。ありがとう。 – Despertar

0

まだ列挙されていない1つの選択肢は、列挙されるデータ要素に加えて、自身にアクセスすることを可能にするラッパーオブジェクトを列挙子に返すことです。サンプルについて:

 
struct ControllableEnumeratorItem<T> 
{ 
    private ControllableEnumerator parent; 
    public T Value {get {return parent.Value;}} 
    public bool MoveNext() {return parent.MoveNext();} 
    public ControllableEnumeratorItem(ControllableEnumerator newParent) 
    {parent = newParent;} 
} 

このアプローチは、(「DeleteCurrentItem」、「AddBeforeCurrentItem」、および「AddAfterCurrentItem」方法を含むことにより)コレクションが列挙中に制御された形で修正することができるようにするデータ構造で使用することができ。

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

    } 
} 
関連する問題