Как получить первый элемент из IEnumerable <T> в .net?

Я часто хочу захватить первый элемент IEnumerable<T> в .net, и я не нашел хороший способ сделать это. Лучшее, что я придумал, это:

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

Тьфу! Итак, есть ли хороший способ сделать это?

Ответ 1

Если вы можете использовать LINQ, вы можете использовать:

var e = enumerable.First();

Это вызовет исключение, но если перечислимое пусто: в этом случае вы можете использовать:

var e = enumerable.FirstOrDefault();

FirstOrDefault() вернет default(T), если перечислимый пуст, который будет null для ссылочных типов или по умолчанию "нулевое значение" для типов значений.

Если вы не можете использовать LINQ, то ваш подход технически корректен и не отличается от создания счетчика с использованием методов GetEnumerator и MoveNext для получения первого результата (этот пример предполагает, что перечисляемый - это IEnumerable<Elem>).

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

Joel Coehoorn упомянул .Single() в комментариях; это также будет работать, если вы ожидаете, что ваш перечислимый будет содержать ровно один элемент - однако он будет генерировать исключение, если он либо пуст, либо больше одного элемента. Существует соответствующий метод SingleOrDefault(), который охватывает этот сценарий аналогично FirstOrDefault(). Тем не менее, David B объясняет, что SingleOrDefault() может по-прежнему выдавать исключение в случае, когда перечисляемый содержит более одного элемента.

Edit: Thanks Марк Гравелл, указав, что мне нужно избавиться от моего объекта IEnumerator после его использования - я редактировал пример не LINQ для покажите ключевое слово using для реализации этого шаблона.

Ответ 2

На всякий случай вы используете .NET 2.0 и не имеете доступа к LINQ:

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

Это должно делать то, что вы ищете... он использует generics, чтобы вы могли получить первый элемент для любого типа 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, тогда уже есть много хороших ответов...

Ответ 3

Ну, вы не указали, какую версию .Net вы используете.

Предполагая, что у вас есть 3.5, другим способом является метод ElementAt:

var e = enumerable.ElementAt(0);

Ответ 4

FirstOrDefault?

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

Ответ 5

Используйте FirstOrDefault или цикл foreach, как уже упоминалось. В ручном режиме выборщик и вызывающий ток следует избегать. foreach будет использовать ваш счетчик для вас, если он реализует IDisposable. При вызове MoveNext и Current вы должны утилизировать его вручную (если это возможно).

Ответ 6

Если ваш IEnumerable не выставляет его <T>, а 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;
}

Ответ 7

попробуйте это

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

Ответ 8

вы также можете попробовать более общую версию, которая дает вам i-й элемент

enumerable.ElementAtOrDefault(я));

надеюсь, что это поможет