C# foreach 带有泛型列表,在使用值类型时检测第一次迭代
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/420774/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
foreach with generic List, detecting first iteration when using value type
提问by Richard Ev
When foreach
ing through a generic list I often want to do something different for the first element in the list:
当foreach
遍历通用列表时,我经常想对列表中的第一个元素做一些不同的事情:
List<object> objs = new List<object>
{
new Object(),
new Object(),
new Object(),
new Object()
};
foreach (object o in objs)
{
if (o == objs.First())
{
System.Diagnostics.Debug.WriteLine("First object - do something special");
}
else
{
System.Diagnostics.Debug.WriteLine("object Do something else");
}
}
This will output:
这将输出:
First object - do something special object Do something else object Do something else object Do something else
This is all fine and dandy.
这一切都很好。
However if my generic list is of a value type, this approach will fail.
但是,如果我的通用列表是值类型,则此方法将失败。
List<int> ints = new List<int> { 0, 0, 0, 0 };
foreach (int i in ints)
{
if (i == ints.First())
{
System.Diagnostics.Debug.WriteLine("First int - do something special");
}
else
{
System.Diagnostics.Debug.WriteLine("int Do something else");
}
}
This will output:
这将输出:
First int - do something special First int - do something special First int - do something special First int - do something special
Now I know I could recode this to add a boolean
flag variable or traditional for
loop, but I am wondering if there's any way to find out if a foreach loop is on the first iteration of its looping.
现在我知道我可以重新编码它以添加boolean
标志变量或传统for
循环,但我想知道是否有任何方法可以确定foreach 循环是否在其循环的第一次迭代中。
采纳答案by Marc Gravell
Well, you could code it using explicit iteration:
好吧,您可以使用显式迭代对其进行编码:
using(var iter = ints.GetEnumerator()) {
if(iter.MoveNext()) {
// do "first" with iter.Current
while(iter.MoveNext()) {
// do something with the rest of the data with iter.Current
}
}
}
The bool flag option (with foreach
) is probably easier though... that is what I (almost) always do!
bool 标志选项(带foreach
)可能更容易……这就是我(几乎)总是做的!
Another option would be LINQ:
另一种选择是 LINQ:
if(ints.Any()) {
var first = ints.First();
// do something with first
}
foreach(var item in ints.Skip(1)) {
// do something with the rest of them
}
The downside of the above is that it tries to look at the list 3 times... since we know it is a list, that is fine - but if all we had was an IEnumerable<T>
, it would only be sensible to iterate it once (since the source might not be re-readable).
上面的缺点是它试图查看列表 3 次......因为我们知道它是一个列表,那很好 - 但是如果我们只有一个IEnumerable<T>
,那么只迭代一次是明智的(因为源可能无法重新读取)。
回答by Amy B
foreach(int i in ints.Take(1))
{
//do first thing
}
foreach(int i in ints.Skip(1))
{
//do other things
}
回答by Jeremy
Instead of if (i == ints.First())
, does it work if you use if (i.Equals(ints.First())
?
而不是 if (i == ints.First())
,如果你使用它是否有效if (i.Equals(ints.First())
?
回答by GWLlosa
The issue you're having is because equality for value types is based on the actual value, not the reference itself. Specifically, in your example, your list is all zeroes, so its asking if currentItem == firstItem, and since they're both zero, this is always true. For this kind of thing, I've always wound up using a boolean flag.
您遇到的问题是因为值类型的相等性基于实际值,而不是引用本身。具体来说,在您的示例中,您的列表全为零,因此它询问 currentItem == firstItem,并且由于它们都为零,因此始终为真。对于这种事情,我总是使用布尔标志。
回答by Steven Robbins
If you really don't want to use a boolean, you could do something like:
如果您真的不想使用布尔值,则可以执行以下操作:
List<int> ints = new List<int> { 0, 0, 0, 0 };
System.Diagnostics.Debug.WriteLine("First int - do something special" + ints.FirstOrDefault().ToString());
foreach (int i in ints.Skip(1))
{
{
System.Diagnostics.Debug.WriteLine("int Do something else");
}
}
But that seems a bit.. wierd :)
但这似乎有点......奇怪:)
回答by Jon Skeet
A while ago I wrote SmartEnumerable(part of MiscUtil) which lets you know if the current element is the first or last, as well as its index. That may help you... it's part of MiscUtil, which is open source - you can take just SmartEnumerable under the same licence, of course.
不久前我写了SmartEnumerable(MiscUtil 的一部分),它让你知道当前元素是第一个还是最后一个,以及它的索引。这可能对您有所帮助...它是 MiscUtil 的一部分,它是开源的 - 当然,您可以在相同的许可证下使用 SmartEnumerable。
Sample code (c'n'p from the web page):
示例代码(来自网页的 c'n'p):
using System;
using System.Collections.Generic;
using MiscUtil.Collections;
class Example
{
static void Main(string[] args)
{
List<string> list = new List<string>();
list.Add("a");
list.Add("b");
list.Add("c");
list.Add("d");
list.Add("e");
foreach (SmartEnumerable<string>.Entry entry in
new SmartEnumerable<string>(list))
{
Console.WriteLine ("{0,-7} {1} ({2}) {3}",
entry.IsLast ? "Last ->" : "",
entry.Value,
entry.Index,
entry.IsFirst ? "<- First" : "");
}
}
}
EDIT: Note that while it works with reference types with distinct references, it'll still fail if you give it a list where the first reference crops up elsewhere in the list.
编辑:请注意,虽然它适用于具有不同引用的引用类型,但如果您给它一个列表,其中第一个引用出现在列表中的其他地方,它仍然会失败。
回答by Daniel Schaffer
Since you're already using LINQ, you could do this:
由于您已经在使用 LINQ,您可以这样做:
var list = new List<Int32>();
// do something with list.First();
foreach (var item in list.Skip(1))
{
// do other stuff
}
回答by Tan
Editing way from another answer:
从另一个答案编辑方式:
IList<object> objs = new List<object>
{
new Object(),
new Object(),
new Object(),
new Object()
};
// to access first element: objs[0]
// to access last element: objs[objs.Count - 1]
System.Diagnostics.Debug.WriteLine("First object - do something special");
foreach (object o in objs.Skip(1))
{
System.Diagnostics.Debug.WriteLine("object Do something else");
}
Reference link about:
参考链接:
Using Skip
: Enumerable.Skip Method (IEnumerable,?Int32)
使用Skip
:Enumerable.Skip 方法 (IEnumerable,?Int32)
Using IList<T>
: List or IList
使用IList<T>
:列表或 IList