你如何找到 For Each (VB.NET) 中的最后一个循环?
Posted
技术标签:
【中文标题】你如何找到 For Each (VB.NET) 中的最后一个循环?【英文标题】:How do you find the last loop in a For Each (VB.NET)? 【发布时间】:2010-09-27 10:43:57 【问题描述】:如何确定我是否处于 VB.NET 中 For Each 语句的最后循环中?
【问题讨论】:
【参考方案1】:通常,您可以在其上执行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 假真【讨论】:
很好的解释,通常是一个很好的解决方案。我要指出,这不适用于某些集合,例如 DataTable.Rows,因为 DataRowCollection 没有属性 .Last。另一种选择是另一个答案中提到的计数器。【参考方案2】:使用 For 循环而不是 ForEach 可能会更容易。但是,类似地,您可以在 ForEach 循环中保留一个计数器,看看它是否等于 yourCollection.Count - 1
,那么您就处于最后一次迭代中。
【讨论】:
好吧,如果我需要这样做,我通常会这样做,但我只是想有一些聪明的方法可以在 For Each 或其他东西中确定这一点。【参考方案3】:使用 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
诀窍是获取当前值的副本,然后让枚举器尝试移动到下一个值。如果失败,则您制作的副本确实是最后一个值,否则还有更多。
【讨论】:
【参考方案4】:简短回答:你不能
长答案: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;
【讨论】:
你忘记了 firstTime=false;在第一次循环迭代之后。 糟糕!感谢您的捕获 - 现在已修复。【参考方案5】:这是一个简单的事情,我不知道它是否在政治上正确,但它有效
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
【参考方案6】:
检查元素是否是容器的最后一个元素。
您为什么要这样做? 您可以在循环之后放置指令。 (你在最后一个元素上执行)
【讨论】:
【参考方案7】:使用 For 循环而不是 ForEach 会更容易,但是您可以执行类似的操作
If item.Equals(itemCollection(itemCollection.Count)) Then
...
End If
在您的 ForEach 循环内...假设对象已正确覆盖 Equals 方法。
这可能比仅使用 For 循环或在单独的变量中跟踪当前索引更耗费资源。
我不确定这是否是正确的语法,我已经很久没有使用VB了。
【讨论】:
【参考方案8】:不能使用标准的“For Each”循环。 “for Each”循环的目的是让您专注于数据而不是基础集合。
【讨论】:
【参考方案9】:我不断回到这篇文章,所以我决定坐下来思考这个问题,然后我想出了这个:
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()
是否会对性能产生很大影响,但如果这会让您夜不能寐,您可以随时将其分配给循环外的变量。
【讨论】:
创意+1。如果您对此获得更多支持,我会将其切换为答案。 我的代码的一个问题是,如果集合多次包含最后一个对象实例,则当前对象可能通过测试,而不管它是否也是序列中的最后一个对象。您可以自行决定这是错误还是功能。出于这个原因,保留一个计数器(遗憾的是)仍然是迄今为止最好的解决方案。【参考方案10】:此代码示例可能会有所帮助
For Each item in Collection
If ReferenceEquals(Collection.Item(Collection.Count - 1), item) Then
'do something with your last item'
End If
Next
【讨论】:
【参考方案11】:如果您绑定到 IEnumerable。你必须在 foreach 循环内吗?如果不是,您可以在 foreach 循环之前声明一个变量。在循环期间设置它。然后,在循环之后使用它(如果它不为空)
【讨论】:
【参考方案12】:好吧,解决方法很少 假设您正在使用 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
【讨论】:
【参考方案13】:For Each xObj In xColl
If xObj = xColl(xColl.Count) Then ...
Next
【讨论】:
inbuild 函数可用于检查循环中的最后一个是 Collection.Last以上是关于你如何找到 For Each (VB.NET) 中的最后一个循环?的主要内容,如果未能解决你的问题,请参考以下文章