27

VB.NET で For Each ステートメントの最終ループにいるかどうかを確認するにはどうすればよいですか?

4

13 に答える 13

60

通常、インターフェイスFor Eachを実装する上で実行できるコレクション。IEnumeratorこのインターフェイスには、 と の 2 つのメソッドと 1 つのプロパティしかありMoveNextませResetCurrent

基本的にFor Each、コレクションに対して a を使用すると、関数が呼び出され、MoveNext返された値が読み取られます。返される値が の場合True、コレクションに有効な要素があり、その要素がCurrentプロパティを介して返されることを意味します。コレクションにそれ以上要素がない場合、MoveNext関数は戻りFalse、反復は終了します。

For Each上記の説明から、がコレクション内の現在の位置を追跡しないことは明らかであるため、質問に対する答えは短いNoです。

ただし、コレクションの最後の要素にいるかどうかを知りたい場合は、次のコードを試すことができます。現在の項目が最後の項目であるかどうかを (LINQ を使用して) チェックします。

For Each item in Collection
    If item Is Collection.Last Then
        'do something with your last item'
    End If
Next

コレクションを呼び出すLast()と、コレクション全体が列挙されることに注意してください。Last()したがって、次のタイプのコレクションを呼び出すことはお勧めしません。

  • ストリーミング コレクション
  • 計算コストの高いコレクション
  • 突然変異の傾向が高いコレクション

このようなコレクションの場合は、(GetEnumerator()関数を介して) コレクションの列挙子を取得することをお勧めします。これにより、アイテムを自分で追跡できます。以下は、アイテムのインデックスと、現在のアイテムがコレクション内の最初のアイテムか最後のアイテムかを生成する拡張メソッドによるサンプル実装です。

<Extension()>
Public Iterator Function EnumerateEx(Of T)(collection As IEnumerable(Of T))
    As IEnumerable(Of (value As T, index As Integer, isFirst As Boolean, isLast As Boolean))

    Using e = collection.GetEnumerator()
        Dim index = -1
        Dim toYield As T

        If e.MoveNext() Then
            index += 1
            toYield = e.Current
        End If

        While e.MoveNext()
            Yield (toYield, index, index = 0, False)
            index += 1
            toYield = e.Current
        End While

        Yield (toYield, index, index = 0, True)
    End Using
End Function

使用例を次に示します。

Sub Main()
    Console.WriteLine("Index   Value   IsFirst   IsLast")
    Console.WriteLine("-----   -----   -------   ------")

    Dim fibonacci = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89}

    For Each i In fibonacci.EnumerateEx()
        Console.WriteLine(String.Join("   ", $"{i.index,5}",
                                             $"{i.value,5}",
                                             $"{i.isFirst,-7}",
                                             $"{i.isLast,-6}"))
    Next

    Console.ReadLine()
End Sub

出力

インデックス値 IsFirst IsLast
----- ----- ---------- ------
    0 0 真偽
    1 1 偽 偽
    2 1 偽 偽
    3 2 偽 偽
    4 3 偽 偽
    5 5 偽 偽
    6 8 偽 偽
    7 13 偽 偽
    8 21 偽 偽
    9 34 偽 偽
   10 55 偽 偽
   11 89 偽 真
于 2010-10-28T10:12:52.170 に答える
14

おそらく、ForEach の代わりに For ループを使用する方が簡単でしょう。ただし、同様に、ForEach ループ内にカウンターを保持し、それが に等しいかどうかを確認するとyourCollection.Count - 1、最後の反復にいます。

于 2008-12-17T23:34:51.157 に答える
7

foreach では、手遅れになるまでこれを知ることはできません (つまり、ループから抜け出します)。

注、IEnumerable インターフェイスしかないものを使用していると仮定しています。リスト、配列などがある場合は、ここで.Countなどを使用してアイテムの数を調べる他の回答に従ってください。これにより、コレクション内のどこにいるかを追跡できます。

ただし、IEnumerable/IEnumerator だけでは、foreach を使用すると、それ以上あるかどうかを確実に知る方法がありません。

これを知る必要がある場合は、自分で IEnumerable を使用してください。これが foreach の機能です。

以下のソリューションは C# 用ですが、VB.NET に簡単に変換できます。

List<Int32> nums = new List<Int32>();
nums.Add(1);
nums.Add(2);
nums.Add(3);

IEnumerator<Int32> enumerator = nums.GetEnumerator();
if (enumerator.MoveNext())
{
    // at least one value available
    while (true)
    {
        // make a copy of it
        Int32 current = enumerator.Current;

        // determine if it was the last value
        // if not, enumerator.Current is now the next value
        if (enumerator.MoveNext())
        {
            Console.Out.WriteLine("not last: " + current);
        }
        else
        {
            Console.Out.WriteLine("last: " + current);
            break;
        }
    }
}
enumerator.Dispose();

これは印刷されます:

not last: 1
not last: 2
last: 3

トリックは、現在の値のコピーを取得し、列挙子に次の値に移動するように依頼することです。それが失敗した場合、作成したコピーは確かに最後の値でした。それ以外の場合は、さらに多くの値があります。

于 2008-12-17T23:41:04.967 に答える
2

簡単な答え: できません

長い答え: For Each ステートメントのセマンティクスには、最初、最後、または特定の反復を実行しているかどうかを識別できるものは何もありません。

For Each は IEnumerable および IEnumerable<> インターフェイスに組み込まれており、動作は呼び出している実装によって異なります。混乱を招きますが、反復しているコレクションが毎回異なる順序で要素を返すことは有効です。幸いなことに、List<> はこれを行いません。

特定の反復が最後であることを本当に知る必要がある場合は、最後の要素を(事前に)特定し、その要素に遭遇したときに別のアクションを実行できます。

最初の反復を (たとえば、ブール値を介して)検出してから、別のことを行う方が簡単です。

例 (C# ですが、VB も同様です):

StringBuilder result = new StringBuilder();
bool firstTime = true;
foreach(string s in names)
{
    if (!firstTime)
    {
        result.Append(", ");
    }

    result.Append(s);
    firstTime = false;
}
于 2008-12-17T23:41:21.330 に答える
1

要素がコンテナーの最後の要素であるかどうかを確認します。

なぜそれをしたいのですか?
ループの後に命令を配置するだけです。(最後の要素で実行すること)

于 2008-12-17T23:29:17.563 に答える
1

ForEach の代わりに For ループを使用する方が簡単ですが、次のようなこともできます。

If item.Equals(itemCollection(itemCollection.Count)) Then
    ...
End If

ForEach ループ内...オブジェクトが Equals メソッドを適切にオーバーライドしたと仮定します。

これはおそらく、単に For ループを使用したり、別の変数で現在のインデックスを追跡したりするよりも、はるかに多くのリソースを消費します。

これが正しい構文かどうかはわかりません。VB を使用してから長い時間が経ちました。

于 2008-12-17T23:41:49.257 に答える
1

標準の「For Each」ループを使用することはできません。「for Each」ループの目的は、基になるコレクションの代わりにデータに集中できるようにすることです。

于 2008-12-17T23:42:03.467 に答える
1

私はこの投稿に何度も戻ってくるので、座ってこの問題について考えることにし、これを思いつきました。

    For Each obj In myCollection
        Console.WriteLine("current object: {0}", obj.ToString)

        If Object.ReferenceEquals(obj, myCollection.Last()) Then
            Console.WriteLine("current object is the last object")
        End If
    Next

If...Then...End If必要に応じて、ステートメントを 1 行に減らすこともできます。すべての反復で呼び出すこと.Last()がパフォーマンスに大きな影響を与えるかどうかはわかりませんが、それが原因で夜目が覚めている場合は、いつでもループ外の変数に割り当てることができます。

于 2013-02-27T21:25:57.703 に答える
0

IEnumerable に縛られている場合。foreach ループ内にいる必要がありますか? そうでない場合は、foreach ループの直前に変数を宣言できます。ループ中に設定してください。次に、ループの後に使用します(nullでない場合)

于 2008-12-17T23:36:34.257 に答える
0

CheckBoxList を使用しているとします。

次に、このコードサンプルが役立つ場合があります:

Dim count As Integer = 0
Dim totalListCount As Integer = CheckBoxList.Items.Count()
For Each li In CheckBoxList.Items
         count += 1
           // This is the Last Item
      If count = totalListCount THEN
          // DO Somthing.....
      END IF
NEXT     
于 2012-07-25T13:54:08.540 に答える