我有一个foreach循环,需要执行一些逻辑时,最后一项是从列表中选择,例如:
foreach (Item result in Model.Results)
{
//if current result is the last item in Model.Results
//then do something in the code
}
我可以知道哪个循环是最后没有使用循环和计数器吗?
我有一个foreach循环,需要执行一些逻辑时,最后一项是从列表中选择,例如:
foreach (Item result in Model.Results)
{
//if current result is the last item in Model.Results
//then do something in the code
}
我可以知道哪个循环是最后没有使用循环和计数器吗?
当前回答
接受的答案将不适用于集合中的副本。如果设置在foreach上,则可以添加自己的索引变量。
int last = Model.Results.Count - 1;
int index = 0;
foreach (Item result in Model.Results)
{
//Do Things
if (index == last)
//Do Things with the last result
index++;
}
其他回答
你可以这样做:
foreach (DataGridViewRow dgr in product_list.Rows)
{
if (dgr.Index == dgr.DataGridView.RowCount - 1)
{
//do something
}
}
正如Chris所展示的,Linq可以工作;只需使用Last()来获得枚举对象中最后一个的引用,只要你没有使用该引用,那么就执行正常的代码,但如果你正在使用该引用,那么就执行额外的事情。它的缺点是它总是O(N)-复杂度。
你可以使用Count()(如果IEnumerable也是一个ICollection, Count()是O(1);这对于大多数常见的内置IEnumerables都是正确的),并将foreach与计数器混合:
var i=0;
var count = Model.Results.Count();
foreach (Item result in Model.Results)
{
if (++i == count) //this is the last item
}
根据@Shimmy的回复,我创建了一个扩展方法,这是每个人都想要的解决方案。它很简单,易于使用,并且只循环遍历集合一次。
internal static class EnumerableExtensions
{
public static void ForEachLast<T>(this IEnumerable<T> collection, Action<T>? actionExceptLast = null, Action<T>? actionOnLast = null)
{
using var enumerator = collection.GetEnumerator();
var isNotLast = enumerator.MoveNext();
while (isNotLast)
{
var current = enumerator.Current;
isNotLast = enumerator.MoveNext();
var action = isNotLast ? actionExceptLast : actionOnLast;
action?.Invoke(current);
}
}
}
这适用于任何IEnumerable< t>。用法如下:
var items = new[] {1, 2, 3, 4, 5};
items.ForEachLast(i => Console.WriteLine($"{i},"), i => Console.WriteLine(i));
输出如下:
1,
2,
3,
4,
5
此外,您还可以将其设置为Select样式方法。然后,在ForEach中重用该扩展。代码是这样的:
internal static class EnumerableExtensions
{
public static void ForEachLast<T>(this IEnumerable<T> collection, Action<T>? actionExceptLast = null, Action<T>? actionOnLast = null) =>
// ReSharper disable once IteratorMethodResultIsIgnored
collection.SelectLast(i => { actionExceptLast?.Invoke(i); return true; }, i => { actionOnLast?.Invoke(i); return true; }).ToArray();
public static IEnumerable<TResult> SelectLast<T, TResult>(this IEnumerable<T> collection, Func<T, TResult>? selectorExceptLast = null, Func<T, TResult>? selectorOnLast = null)
{
using var enumerator = collection.GetEnumerator();
var isNotLast = enumerator.MoveNext();
while (isNotLast)
{
var current = enumerator.Current;
isNotLast = enumerator.MoveNext();
var selector = isNotLast ? selectorExceptLast : selectorOnLast;
//https://stackoverflow.com/a/32580613/294804
if (selector != null)
{
yield return selector.Invoke(current);
}
}
}
}
正如Shimmy所指出的,使用Last()可能会导致性能问题,例如,如果您的集合是LINQ表达式的实时结果。为了防止多次迭代,你可以使用“ForEach”扩展方法,如下所示:
var elements = new[] { "A", "B", "C" };
elements.ForEach((element, info) => {
if (!info.IsLast) {
Console.WriteLine(element);
} else {
Console.WriteLine("Last one: " + element);
}
});
扩展方法看起来像这样(作为额外的奖励,它还会告诉你索引,如果你在看第一个元素):
public static class EnumerableExtensions {
public delegate void ElementAction<in T>(T element, ElementInfo info);
public static void ForEach<T>(this IEnumerable<T> elements, ElementAction<T> action) {
using (IEnumerator<T> enumerator = elements.GetEnumerator())
{
bool isFirst = true;
bool hasNext = enumerator.MoveNext();
int index = 0;
while (hasNext)
{
T current = enumerator.Current;
hasNext = enumerator.MoveNext();
action(current, new ElementInfo(index, isFirst, !hasNext));
isFirst = false;
index++;
}
}
}
public struct ElementInfo {
public ElementInfo(int index, bool isFirst, bool isLast)
: this() {
Index = index;
IsFirst = isFirst;
IsLast = isLast;
}
public int Index { get; private set; }
public bool IsFirst { get; private set; }
public bool IsLast { get; private set; }
}
}
只需存储之前的值,并在循环中使用它。然后在最后,“previous”值将是最后一项,让您以不同的方式处理它。不需要计数或特殊库。
bool empty = true;
Item previousItem;
foreach (Item result in Model.Results)
{
// Alternatively, check if previousItem == null
// if your Enumerable can't contain nulls
if (!empty)
{
// We know this isn't the last item because
// it came from the previous iteration
handleRegularItem(previousItem);
}
previousItem = result;
empty = false;
}
if (!empty)
{
// We know this is the last item because the loop is finished
handleLastItem(previousItem);
}