C# 使用泛型列表的foreach,在使用值类型时检测第一次迭代

C# 使用泛型列表的foreach,在使用值类型时检测第一次迭代,c#,.net,C#,.net,当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.Diagn

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");
    }
}
List objs=新列表
{
新对象(),
新对象(),
新对象(),
新对象()
};
foreach(objs中的对象o)
{
如果(o==objs.First())
{
System.Diagnostics.Debug.WriteLine(“第一个对象-做一些特殊的事情”);
}
其他的
{
System.Diagnostics.Debug.WriteLine(“对象执行其他操作”);
}
}
这将输出:

First object - do something special object Do something else object Do something else object Do something else First int - do something special First int - do something special First int - do something special First int - do something special 第一个目标-做一些特别的事情 对象做其他事情 对象做其他事情 对象做其他事情 这一切都很好

但是,如果我的泛型列表是值类型,那么这种方法将失败

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");
    }
}
List ints=新列表{0,0,0,0};
foreach(整数中的整数i)
{
如果(i==ints.First())
{
System.Diagnostics.Debug.WriteLine(“第一个int-do-some-special”);
}
其他的
{
System.Diagnostics.Debug.WriteLine(“int做其他事情”);
}
}
这将输出:

First object - do something special object Do something else object Do something else object Do something else First int - do something special First int - do something special First int - do something special First int - do something special 首先,做一些特别的事情 首先,做一些特别的事情 首先,做一些特别的事情 首先,做一些特别的事情
现在我知道我可以对其重新编码,为循环添加一个
布尔
标志变量或传统的
,但我想知道是否有任何方法可以确定foreach循环是否在其循环的第一次迭代中。

可以使用显式迭代对其进行编码:

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
     }
  }
}
bool标志选项(带有
foreach
)可能更简单,不过。。。这就是我(几乎)经常做的事

另一种选择是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
}

上面的缺点是,它试图查看列表3次。。。因为我们知道它是一个列表,这很好——但是如果我们只有一个
IEnumerable
,那么只需要迭代一次(因为源代码可能无法重新读取)。

而不是if
(i==ints.First())
,如果使用
if(i.Equals(ints.First())是否有效

您遇到的问题是,值类型的相等是基于实际值,而不是引用本身。具体地说,在您的示例中,您的列表都是零,因此它询问currentItem==firstItem是否为零,因为它们都为零,所以这总是正确的。对于这种情况,我总是使用布尔标志

如果您真的不想使用布尔值,可以执行以下操作:

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");
    }
}
List ints=新列表{0,0,0,0};
System.Diagnostics.Debug.WriteLine(“First int-do some special”+ints.FirstOrDefault().ToString());
foreach(整数跳过(1)中的整数i)
{
{
System.Diagnostics.Debug.WriteLine(“int做其他事情”);
}
}
但这似乎有点..wierd:)

不久前我写了一篇文章(MiscUtil的一部分),它让您知道当前元素是第一个还是最后一个,以及它的索引。那可能会帮助你。。。它是MiscUtil的一部分,它是开源的——当然,您可以在同一个许可证下使用SmartNumerable

示例代码(网页上的c'n'p):

使用系统;
使用System.Collections.Generic;
使用MiscUtil.Collections;
课例
{
静态void Main(字符串[]参数)
{
列表=新列表();
列表。添加(“a”);
列表。添加(“b”);
列表。添加(“c”);
列表。添加(“d”);
列表。添加(“e”);
foreach(智能可数.Entry in
新智能数字(列表))
{
Console.WriteLine(“{0,-7}{1}({2}{3}”),
entry.IsLast?“Last->”:“,
条目。值,
条目索引,

entry.IsFirst?”因为您已经在使用LINQ,所以可以执行以下操作:

var list = new List<Int32>();

// do something with list.First();

foreach (var item in list.Skip(1))
{
    // do other stuff
}
var list=newlist();
//用list.First()做一些事情;
foreach(列表中的变量项。跳过(1))
{
//做其他事情
}

另一个答案的编辑方式:

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");
}
IList objs=新列表
{
新对象(),
新对象(),
新对象(),
新对象()
};
//要访问第一个元素:objs[0]
//访问最后一个元素:objs[objs.Count-1]
System.Diagnostics.Debug.WriteLine(“第一个对象-做一些特殊的事情”);
foreach(objs.Skip(1)中的对象o)
{
System.Diagnostics.Debug.WriteLine(“对象执行其他操作”);
}
参考链接关于:

使用
跳过

使用
IList

Select()
可以返回当前元素的索引。通过简单的扩展方法,可以执行以下操作:

var ints = new List<int> { 1, 2, 3, 4 };

foreach (var item in ints.SelectWithIndex())
{
    if (item.index == 0)
        Console.WriteLine($"First: {item.val}");
    else
        Console.WriteLine($"{item.val}");
}

不,关键是所有元素都是相同的-每个元素都等于第一个元素。如果列表包含相同的引用两次,引用类型也会发生相同的情况。我怀疑结果会是相同的,因为值类型的相等性是由它们的值决定的,而不是由引用决定的。Jon Skeet的《忍者》具有更好的答案。一个完美的“var”候选者,如果我见过的话;-pAlso需要一个ForEach重载来执行一个操作,或者一个操作-即list.ForEach((index,isLast,value)=>{…});var:绝对可以。当然写在C#3之前:)并同意采取行动或至少采取行动。更多关于更多LINQ项目……是的……有点笨拙的bool用法就是为什么我没有包括“isFirst”(让“index”涵盖这一点)……知道你是否在最后一个项目上有多大价值是一个有趣的问题(我只是试图涵盖上面示例中的所有可用选项)@JonSkeet这样的东西真的应该内置到.NET中,虽然手动掷骰并不太难。但是“bool flag”选项是什么(使用foreach)可能更容易“我的意思是,如何使用布尔标志来检测第一次迭代,特别是如果必须对一组3个或更多嵌套的foreach循环执行此操作???@denas
bool first=true;
在您希望“first”为的任何级别,并在每次迭代后将其设置为false?”
static class MyExtensions
{
    public static IEnumerable<(T val, int index)> SelectWithIndex<T>(this List<T> list)
    {
        return list.Select((i, j) => CreateValIndex(i, j));
    }

    public static (T val, int index) CreateValIndex<T>(T val, int index)
    {
        return (val, index);
    }
}