Foreach循环,确定哪个是循环的最后一次迭代

mis*_*hap 216 c# asp.net foreach

我有一个foreach循环,需要在从中选择最后一项时执行一些逻辑List,例如:

 foreach (Item result in Model.Results)
 {
      //if current result is the last item in Model.Results
      //then do something in the code
 }
Run Code Online (Sandbox Code Playgroud)

如果不使用for循环和计数器,我可以知道哪个循环是最后的吗?

Chr*_*isF 264

如果你只需要对最后一个元素做一些事情(而不是与最后一个元素不同的东西,那么使用LINQ将有助于:

Item last = Model.Results.Last();
// do something with last
Run Code Online (Sandbox Code Playgroud)

如果您需要对最后一个元素做一些不同的事情,那么您需要以下内容:

Item last = Model.Results.Last();
foreach (Item result in Model.Results)
{
    // do something with each item
    if (result.Equals(last))
    {
        // do something different with the last item
    }
    else
    {
        // do something different with every item but the last
    }
}
Run Code Online (Sandbox Code Playgroud)

虽然您可能需要编写自定义比较器,以确保您可以告诉该项目与返回的项目相同Last().

应谨慎使用此方法,因为Last可能必须遍历集合.虽然这对于小型集合来说可能不是问题,但如果它变大,它可能会对性能产生影响.如果列表包含重复项,它也将失败.在这种情况下,这样的事情可能更合适:

int totalCount = result.Count();
for (int count = 0; count < totalCount; count++)
{
    Item result = Model.Results[count];
    count++;
    // do something with each item
    if (count == totalCount)
    {
        // do something different with the last item
    }
    else
    {
        // do something different with every item but the last
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您的集合中有重复项,则这不起作用.例如,如果您正在处理字符串集合,并且存在任何重复项,则对于列表中最后一项的每次出现都将执行"与最后一项不同"代码. (51认同)
  • 你的代码将通过整个集合迭代两次 - 如果集合不小则很糟糕.见[this](http://stackoverflow.com/a/17460853/75500)答案. (10认同)
  • 这个答案是陈旧的,但是对于其他人来看这个答案,你可以得到最后一个元素,并确保你不必通过使用循环元素:Item last = Model.Results [Model.Results.Count - 1] count列表的属性不需要循环.如果列表中有重复项,那么只需在for循环中使用迭代器变量.常规的旧for循环也不错. (6认同)

Fio*_*ite 168

一个好的老式循环怎么样?

for (int i = 0; i < Model.Results.Count; i++) {

     if (i == Model.Results.Count - 1) {
           // this is the last item
     }
}
Run Code Online (Sandbox Code Playgroud)

或者使用Linq和foreach:

foreach (Item result in Model.Results)   
{   
     if (Model.Results.IndexOf(result) == Model.Results.Count - 1) {
             // this is the last item
     }
}
Run Code Online (Sandbox Code Playgroud)

  • 当for循环完全能够做到这一点时,很多人都在试图解决这样一个简单的问题.:\ (12认同)
  • 不幸的是,如果“Model.Results”是“IEnumerable”,则无法使用这个聪明的解决方案。您可以在循环之前调用“Count()”,但这可能会导致序列的完整迭代。 (5认同)
  • 任何想要对字符串(或值类型)集合使用 LINQ 解决方案的人请注意:它通常不起作用,因为如果列表中的最后一个字符串也出现在列表的前面,则 == 比较将失败。仅当您使用保证没有重复字符串的列表时,它才有效。 (2认同)

Kei*_*thS 37

正如克里斯所说,Linq将会工作; 只需使用Last()来获取对枚举中最后一个的引用,并且只要您不使用该引用然后执行正常的代码,但是如果您正在使用该引用,那么请执行额外的操作.它的缺点是它总是O(N) - 复杂性.

您可以使用Count()(如果IEnumerable也是ICollection,则为O(1);对于大多数常见的内置IEnumebles,这是正确的),并将foreach与计数器混合:

var i=0;
var count = Model.Results.Count();
foreach (Item result in Model.Results)
 {
      if(++i==count) //this is the last item
 }
Run Code Online (Sandbox Code Playgroud)


Shi*_*mmy 37

Last()在某些类型上使用将循环通过整个集合!
意思是如果你foreach打电话Last(),你就打了两次!我敢肯定你想在大集合中避免.

然后解决方案是使用do while循环:

using (var enumerator = collection.GetEnumerator())
{

  var last = !enumerator.MoveNext();
  T current;

  while(!last)
  {
    current = enumerator.Current;        

    //process item

    last = !enumerator.MoveNext();        

    //process item extension according to flag; flag means item

  }
}
Run Code Online (Sandbox Code Playgroud)

测试

除非集合类型是类型IList<T>,否则Last()函数将遍历所有集合元素.

  • IEnumerator 没有实现 IDisposable,因此 using with 的行会引发编译时错误!+1 对于解决方案,大多数时候我们不能简单地使用 for 代替 foreach ,因为可枚举集合项在运行时计算或者序列不支持随机访问。 (2认同)
  • [通用](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ienumerator-1?view=netstandard-1.0) 可以。 (2认同)

小智 19

foreach (var item in objList)
{
  if(objList.LastOrDefault().Equals(item))
  {

  }
}
Run Code Online (Sandbox Code Playgroud)

  • 不好的答案。n^2 复杂度而不是 n。 (2认同)
  • 由于@ShimmyWeitzhandler 提到的问题,这是不正确的,不应使用。通常期望在循环之外准备语句的所有此类值。 (2认同)

Dan*_*olf 10

正如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);
    }
});
Run Code Online (Sandbox Code Playgroud)

扩展方法看起来像这样(作为一个额外的奖励,它还会告诉你索引,如果你正在查看第一个元素):

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; }
    }
}
Run Code Online (Sandbox Code Playgroud)


Mat*_*eid 6

迭代器实现没有提供.您的集合可能IList是可通过O(1)中的索引访问的集合.在这种情况下,您可以使用正常的for循环:

for(int i = 0; i < Model.Results.Count; i++)
{
  if(i == Model.Results.Count - 1) doMagic();
}
Run Code Online (Sandbox Code Playgroud)

如果你知道计数,而是通过指标(因此,结果是不能访问ICollection),你可以通过增加一个计数自己iforeach的身体,它比较长.

这一切都不是很优雅.克里斯的解决方案可能是迄今为止我见过的最好的解决方案.


Dus*_*ges 5

最好的方法可能只是在循环后执行该步骤:例如

foreach(Item result in Model.Results)
{
   //loop logic
}

//Post execution logic
Run Code Online (Sandbox Code Playgroud)

或者如果你需要对最后的结果做些什么

foreach(Item result in Model.Results)
{
   //loop logic
}

Item lastItem = Model.Results[Model.Results.Count - 1];

//Execute logic on lastItem here
Run Code Online (Sandbox Code Playgroud)


小智 5

怎么样简单的方法.

Item last = null;
foreach (Item result in Model.Results)
{
    // do something with each item

    last = result;
}

//Here Item 'last' contains the last object that came in the last of foreach loop.
DoSomethingOnLastElement(last);
Run Code Online (Sandbox Code Playgroud)

  • 我不知道为什么有人下来投票给你.考虑到你已经在执行foreach并且产生o(n)的成本,这是完全可以接受的. (2认同)
  • 尽管答案很适合找出最后一个项目,但**它不符合OP的条件**“ * ...,确定哪个是循环的最后迭代*”。因此,您无法确定最后一次迭代实际上是最后一次迭代,因此,您无法以不同的方式处理它,甚至无法忽略它。这就是有人拒绝您的原因。@arviman,您对此很好奇。 (2认同)

Fab*_*doy 5

进一步提高Daniel Wolf的答案,您可以将其堆叠在一起,IEnumerable以避免多次迭代和lambda,例如:

var elements = new[] { "A", "B", "C" };
foreach (var e in elements.Detailed())
{
    if (!e.IsLast) {
        Console.WriteLine(e.Value);
    } else {
        Console.WriteLine("Last one: " + e.Value);
    }
}
Run Code Online (Sandbox Code Playgroud)

扩展方法的实现:

public static class EnumerableExtensions {
    public static IEnumerable<IterationElement<T>> Detailed<T>(this IEnumerable<T> source)
    {
        if (source == null)
            throw new ArgumentNullException(nameof(source));

        using (var enumerator = source.GetEnumerator())
        {
            bool isFirst = true;
            bool hasNext = enumerator.MoveNext();
            int index = 0;
            while (hasNext)
            {
                T current = enumerator.Current;
                hasNext = enumerator.MoveNext();
                yield return new IterationElement<T>(index, current, isFirst, !hasNext);
                isFirst = false;
                index++;
            }
        }
    }

    public struct IterationElement<T>
    {
        public int Index { get; }
        public bool IsFirst { get; }
        public bool IsLast { get; }
        public T Value { get; }

        public IterationElement(int index, T value, bool isFirst, bool isLast)
        {
            Index = index;
            IsFirst = isFirst;
            IsLast = isLast;
            Value = value;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 另一个答案不会多次迭代源,因此这不是您要解决的问题。您确实允许使用“foreach”,这是一个改进。 (2认同)