我有一个foreach
循环,并需要在List
中选择最后一项时执行一些逻辑,例如:
foreach (Item result in Model.Results)
{
//if current result is the last item in Model.Results
//then do something in the code
}
我可以在不使用for循环和计数器的情况下知道哪个循环是最后一个吗?
我有一个foreach
循环,并需要在List
中选择最后一项时执行一些逻辑,例如:
foreach (Item result in Model.Results)
{
//if current result is the last item in Model.Results
//then do something in the code
}
我可以在不使用for循环和计数器的情况下知道哪个循环是最后一个吗?
如果你只是需要对最后一个元素做一些操作(而不是对最后一个元素做一些不同的操作),那么使用LINQ会有所帮助:
Item last = Model.Results.Last();
// do something with last
如果你需要对最后一个元素进行不同的操作,那么你需要像这样做: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
}
}
虽然您可能需要编写自定义比较器来确保您能够确定该项与 Last()
返回的项相同,但这种方法应该谨慎使用。
这种方法应该谨慎使用,因为 Last
可能需要遍历整个集合。虽然对于小集合可能没有问题,但如果集合很大,它可能会影响性能。如果列表包含重复的项,它也将失败。在这种情况下,可能更适合像这样的方法:
int totalCount = result.Count();
for (int count = 0; count < totalCount; count++)
{
Item result = Model.Results[count];
// do something with each item
if ((count + 1) == totalCount)
{
// do something different with the last item
}
else
{
// do something different with every item but the last
}
}
foreach (Item result in Model.Results)
{
if (result == Model.Results.Last())
{
last;
}
看起来你的意思基本相同。 - mishapvar last = Model.Result[Model.Result.Count - 1];
而不是使用 Last()
,因为前者更快。 - Tân那用一种传统的for循环怎么样?
for (int i = 0; i < Model.Results.Count; i++) {
if (i == Model.Results.Count - 1) {
// this is the last item
}
}
或者使用 Linq 和 foreach:
foreach (Item result in Model.Results)
{
if (Model.Results.IndexOf(result) == Model.Results.Count - 1) {
// this is the last item
}
}
Model.Results
是一个 IEnumerable
,那么您无法使用这个巧妙的解决方案。您可以在循环之前调用 Count()
,但这可能会导致对整个序列进行完整迭代。 - Luca CremonesiLast()
在某些类型上使用会循环遍历整个集合!这意味着如果你使用 foreach
并调用 Last()
,你将会循环两次!对于大型集合来说,这是需要避免的。
解决方案是使用 while
循环:
using var enumerator = collection.GetEnumerator();
var last = !enumerator.MoveNext();
T current;
while (!last)
{
current = enumerator.Current;
//process item
last = !enumerator.MoveNext();
if(last)
{
//additional processing for last item
}
}
因此,除非集合类型为 IList<T>
类型,否则 Last()
函数将遍历所有集合元素。
如果您的集合提供随机访问(例如实现了 IList<T>
),您还可以按如下方式检查您的项。
if(collection is IList<T> list)
return collection[^1]; //replace with collection.Count -1 in pre-C#8 apps
using
语句吗?我认为只有在对象处理操作系统资源时才需要,而不是针对托管数据结构。 - Crouching Kitten正如Chris所展示的那样,Linq可以解决这个问题;只需使用Last()获取可枚举对象中的最后一个引用,只要您不使用该引用,就可以继续进行正常的代码编写,但如果您正在使用该引用,则需要执行额外的操作。缺点是它的时间复杂度始终为O(N)。
您还可以使用Count()(如果IEnumerable也是ICollection,则为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
}
var last = objList.LastOrDefault();
foreach (var item in objList)
{
if (item.Equals(last))
{
}
}
foreach
块之前应该只找到最后一个项目一次(促进记忆化)。像这样:var lastItem = objList.LastOrDeafault();
。然后从foreach
循环的内部,您可以这样检查它:f (item.Equals(lastItem)) { ... }
。在您原始的答案中,objList.LastOrDefault()
将在每个“foreach”迭代中迭代集合(涉及多项式复杂度)。 - AlexMelw正如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; }
}
}
进一步改进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);
}
}
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;
}
}
}
foreach
,这是一种改进。 - Servy迭代器实现并没有提供这个功能。你的集合可能是一个可以通过O(1)索引访问的IList
。在这种情况下,你可以使用普通的for
循环:
for(int i = 0; i < Model.Results.Count; i++)
{
if(i == Model.Results.Count - 1) doMagic();
}
ICollection
),你可以通过在foreach
的主体中递增i
并将其与长度进行比较来计数。foreach(Item result in Model.Results)
{
//loop logic
}
//Post execution logic
foreach(Item result in Model.Results)
{
//loop logic
}
Item lastItem = Model.Results[Model.Results.Count - 1];
//Execute logic on lastItem here
那么,有没有更简单的方法呢?
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);