【发布时间】:2010-09-27 10:43:57
【问题描述】:
如何确定我是否处于 VB.NET 中 For Each 语句的最后循环中?
【问题讨论】:
标签: vb.net foreach for-loop each
如何确定我是否处于 VB.NET 中 For Each 语句的最后循环中?
【问题讨论】:
标签: vb.net foreach for-loop each
通常,您可以在其上执行For Each 的集合实现IEnumerator 接口。这个接口只有两个方法MoveNext和Reset和一个属性Current。
基本上,当您在集合上使用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
了解对集合调用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 假真【讨论】:
使用 For 循环而不是 ForEach 可能会更容易。但是,类似地,您可以在 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 语句的语义中没有任何内容可以让您确定您是在运行第一次、最后一次还是任何特定的迭代。
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;
}
【讨论】:
这是一个简单的事情,我不知道它是否在政治上正确,但它有效
for each item in listOfThings
if(item = listOfThings.last)then
'you found the last loop
end if
next
【讨论】:
is 关键字来检查。 if item is listOfThings.last then
检查元素是否是容器的最后一个元素。
您为什么要这样做?
您可以在循环之后放置指令。 (你在最后一个元素上执行)
【讨论】:
使用 For 循环而不是 ForEach 会更容易,但是您可以执行类似的操作
If item.Equals(itemCollection(itemCollection.Count)) Then
...
End If
在您的 ForEach 循环内...假设对象已正确覆盖 Equals 方法。
这可能比仅使用 For 循环或在单独的变量中跟踪当前索引更耗费资源。
我不确定这是否是正确的语法,我已经很久没有使用VB了。
【讨论】:
不能使用标准的“For Each”循环。 “for Each”循环的目的是让您专注于数据而不是基础集合。
【讨论】:
我不断回到这篇文章,所以我决定坐下来思考这个问题,然后我想出了这个:
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 语句减少为一行。我不确定在每次迭代中调用 .Last() 是否会对性能产生很大影响,但如果这会让您夜不能寐,您可以随时将其分配给循环外的变量。
【讨论】:
此代码示例可能会有所帮助
For Each item in Collection
If ReferenceEquals(Collection.Item(Collection.Count - 1), item) Then
'do something with your last item'
End If
Next
【讨论】:
如果您绑定到 IEnumerable。你必须在 foreach 循环内吗?如果不是,您可以在 foreach 循环之前声明一个变量。在循环期间设置它。然后,在循环之后使用它(如果它不为空)
【讨论】:
好吧,解决方法很少 假设您正在使用 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
【讨论】:
For Each xObj In xColl
If xObj = xColl(xColl.Count) Then ...
Next
【讨论】: