【问题标题】:How do I get the first element from an IEnumerable<T> in .net?如何从 .net 中的 IEnumerable<T> 获取第一个元素?
【发布时间】:2009-01-30 21:12:21
【问题描述】:

我经常想在 .net 中获取 IEnumerable&lt;T&gt; 的第一个元素,但我还没有找到一个好的方法。我想出的最好的是:

foreach(Elem e in enumerable) {
  // do something with e
  break;
}

呸!那么,有什么好的方法可以做到这一点吗?

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    如果你可以使用 LINQ,你可以使用:

    var e = enumerable.First();
    

    如果 enumerable 为空,这将引发异常:在这种情况下,您可以使用:

    var e = enumerable.FirstOrDefault();
    

    如果枚举为空,FirstOrDefault() 将返回 default(T),引用类型为 null,值类型为默认的“零值”。

    如果您不能使用 LINQ,那么您的方法在技术上是正确的,并且与使用 GetEnumeratorMoveNext 方法创建枚举器来检索第一个结果没有什么不同(此示例假定 enumerable 是 IEnumerable&lt;Elem&gt;) :

    Elem e = myDefault;
    using (IEnumerator<Elem> enumer = enumerable.GetEnumerator()) {
        if (enumer.MoveNext()) e = enumer.Current;
    }
    

    Joel Coehoorn 在 cmets 中提到了.Single();如果您希望您的可枚举仅包含一个元素,这也将起作用 - 但是如果它为空或大于一个元素,它将引发异常。有一个对应的SingleOrDefault() 方法以与FirstOrDefault() 类似的方式涵盖此场景。但是,David B 解释说,SingleOrDefault() 在可枚举包含多个项目的情况下仍可能引发异常。

    编辑:感谢Marc Gravell 指出我需要在使用IEnumerator 对象后处理它 - 我已经编辑了非LINQ 示例以显示using 关键字来实现此模式。

    【讨论】:

    • 值得指出的是 SingleOrDefault 将返回 1) 如果只有一个项目,则返回唯一的项目。 2) 如果没有项目,则为 null。 3) 如果有多个项目,则抛出异常。
    • 好电话 David B - 添加了注释。
    【解决方案2】:

    以防万一您使用的是 .NET 2.0 并且无权访问 LINQ:

     static T First<T>(IEnumerable<T> items)
     {
         using(IEnumerator<T> iter = items.GetEnumerator())
         {
             iter.MoveNext();
             return iter.Current;
         }
     }
    

    这应该可以满足您的需求...它使用泛型,因此您可以获取任何类型 IEnumerable 的第一项。

    这样称呼它:

    List<string> items = new List<string>() { "A", "B", "C", "D", "E" };
    string firstItem = First<string>(items);
    

    或者

    int[] items = new int[] { 1, 2, 3, 4, 5 };
    int firstItem = First<int>(items);
    

    您可以轻松地对其进行修改以模仿 .NET 3.5 的 IEnumerable.ElementAt() 扩展方法:

    static T ElementAt<T>(IEnumerable<T> items, int index)
    {
        using(IEnumerator<T> iter = items.GetEnumerator())
        {
            for (int i = 0; i <= index; i++, iter.MoveNext()) ;
            return iter.Current;
        }
    } 
    

    这样称呼它:

    int[] items = { 1, 2, 3, 4, 5 };
    int elemIdx = 3;
    int item = ElementAt<int>(items, elemIdx);
    

    当然,如果您确实可以访问 LINQ,那么已经发布了很多好的答案...

    【讨论】:

    • 哎呀,你当然是对的,谢谢。我已经更正了我的示例。
    • 如果你使用的是 2.0,你也没有 var。
    • 好吧,我想如果你想挑剔,我会正确声明它们:P
    • 作为一个使用 .NET 2.0 的人,非常感谢!赞一个。
    【解决方案3】:

    嗯,您没有指定您使用的 .Net 版本。

    假设你有 3.5,另一种方法是 ElementAt 方法:

    var e = enumerable.ElementAt(0);
    

    【讨论】:

    • ElementAt(0),漂亮又简单。
    【解决方案4】:

    FirstOrDefault?

    Elem e = enumerable.FirstOrDefault();
    //do something with e
    

    【讨论】:

      【解决方案5】:

      试试这个

      IEnumberable<string> aa;
      string a = (from t in aa where t.Equals("") select t.Value).ToArray()[0];
      

      【讨论】:

        【解决方案6】:

        如前所述,使用 FirstOrDefault 或 foreach 循环。应避免手动获取枚举器并调用 Current。如果 foreach 实现 IDisposable,它将为您处理您的枚举器。调用 MoveNext 和 Current 时,您必须手动处理它(如果适用)。

        【讨论】:

        • 有什么证据表明应该避免使用枚举数?在我的机器上进行的性能测试表明它比 foreach 有大约 10% 的性能提升。
        • 这取决于您枚举的内容。 Erumerator 可以关闭与数据库的连接,刷新和关闭文件句柄,释放一些锁定的对象等。我猜不处理某个整数列表的枚举器不会有害。
        【解决方案7】:

        如果您的 IEnumerable 没有公开它的 &lt;T&gt; 并且 Linq 失败,您可以使用反射编写一个方法:

        public static T GetEnumeratedItem<T>(Object items, int index) where T : class
        {
          T item = null;
          if (items != null)
          {
            System.Reflection.MethodInfo mi = items.GetType()
              .GetMethod("GetEnumerator");
            if (mi != null)
            {
              object o = mi.Invoke(items, null);
              if (o != null)
              {
                System.Reflection.MethodInfo mn = o.GetType()
                  .GetMethod("MoveNext");
                if (mn != null)
                {
                  object next = mn.Invoke(o, null);
                  while (next != null && next.ToString() == "True")
                  {
                    if (index < 1)
                    {
                      System.Reflection.PropertyInfo pi = o
                        .GetType().GetProperty("Current");
                      if (pi != null) item = pi
                        .GetValue(o, null) as T;
                      break;
                    }
                    index--;
                  }
                }
              }
            }
          }
          return item;
        }
        

        【讨论】:

          【解决方案8】:

          您也可以尝试更通用的版本,它为您提供第 i 个元素

          enumerable.ElementAtOrDefault(i));

          希望对你有帮助

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-04-27
            • 1970-01-01
            • 2021-12-08
            • 1970-01-01
            • 2018-02-09
            • 1970-01-01
            • 2017-12-24
            相关资源
            最近更新 更多