VB.NETのForEachステートメントの最後のループに入っているかどうかを確認するにはどうすればよいですか?
一般に、For Each
を実行できるコレクションは、 IEnumerator
インターフェイスを実装します。このインターフェースには、MoveNext
とReset
の2つのメソッドと、Current
の1つのプロパティしかありません。
基本的に、コレクションでFor Each
を使用すると、コレクションはMoveNext
関数を呼び出し、返された値を読み取ります。返される値がTrue
の場合、コレクションに有効な要素があり、要素はCurrent
プロパティを介して返されます。コレクションにこれ以上要素がない場合、MoveNext
関数はFalse
を返し、反復は終了します。
上記の説明から、For Each
がコレクション内の現在の位置を追跡していないことは明らかであるため、質問に対する答えは短いですいいえ。
ただし、コレクションの最後の要素にいるのかどうかを知りたい場合は、次のコードを試すことができます。現在のアイテムが最後のアイテムであるかどうかを(LINQを使用して)チェックします。
For Each item in Collection
If item Is Collection.Last Then
'do something with your last item'
End If
Next
ForEachの代わりにForループを使用する方がおそらく簡単でしょう。ただし、同様に、ForEachループ内にカウンターを保持し、それがyourCollection.Count - 1
に等しいかどうかを確認すると、最後の反復になります。
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
秘訣は、現在の値のコピーを取り、列挙子に次の値に移動するように依頼することです。それが失敗した場合、あなたが作成したコピーは確かに最後の値でした、そうでなければもっとあります。
簡単な答え:できません
長い答え:For Eachステートメントのセマンティクスには、最初、最後、または特定の反復を実行しているかどうかを識別することを可能にするものは何もありません。
ForEachは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;
}
私はこの投稿に戻ってくるので、座ってこの問題について考えることにし、これを思いついた:
_ 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()
を呼び出すことがパフォーマンスに大きな影響を与えるかどうかはわかりませんが、それが夜に目を覚まし続けるのであれば、ループ外の変数にいつでも割り当てることができます。
要素がコンテナの最後の要素であるかどうかを確認します。
なぜあなたはそれをしたいのですか?
ループの後に命令を配置するだけで済みます。 (最後の要素で実行すること)
ForEachの代わりにForループを使用する方が簡単ですが、次のようなことができます。
If item.Equals(itemCollection(itemCollection.Count)) Then
...
End If
forEachループの内部...オブジェクトがEqualsメソッドを適切にオーバーライドしたと仮定します。
これは、Forループを使用したり、別の変数で現在のインデックスを追跡したりするよりも、おそらくはるかに多くのリソースを消費します。
これが正しい構文かどうかはわかりません。VBを使用してから久しぶりです。
標準の「ForEach」ループを使用することはできません。 「forEach」ループの目的は、基になるコレクションの代わりにデータに集中できるようにすることです。
このコードサンプルが役立つかもしれません
For Each item in Collection
If ReferenceEquals(Collection.Item(Collection.Count - 1), item) Then
'do something with your last item'
End If
Next
回避策はほとんどありません。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
IEnumerableに関連付けられている場合。 foreachループ内にいる必要がありますか?そうでない場合は、foreachループの直前に変数を宣言できます。ループ中に設定します。次に、ループの後に使用します(nullでない場合)
これは私が政治的に正しいかどうかわからない簡単なことですが、それは機能します
for each item in listOfThings
if(item = listOfThings.last)then
'you found the last loop
end if
next