Ответы:
Если вы можете использовать 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;
}
Джоэл Кохорн упоминается .Single()в комментариях; это также сработает, если вы ожидаете, что ваш перечисляемый объект будет содержать ровно один элемент - однако он выдаст исключение, если он пустой или больше одного элемента. Существует соответствующий SingleOrDefault()метод, который охватывает этот сценарий аналогично FirstOrDefault(). Тем не менее, Дэвид Б. объясняет, что SingleOrDefault()все равно может вызвать исключение в случае, когда перечислимое содержит более одного элемента.
Редактировать: Спасибо Marc Gravell за указание, что мне нужно избавиться от моего IEnumeratorобъекта после его использования - я отредактировал не-LINQ пример, чтобы отобразить usingключевое слово для реализации этого шаблона.
На тот случай, если вы используете .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);
Вы можете изменить его достаточно легко, чтобы имитировать метод расширения IEnumerable.ElementAt () .NET 3.5:
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, то есть много хороших ответов уже отвечал ...
Ну, вы не указали, какую версию .Net вы используете.
Предполагая, что у вас есть 3.5, другой способ - это метод ElementAt:
var e = enumerable.ElementAt(0);
Elem e = enumerable.FirstOrDefault();
//do something with e
Используйте FirstOrDefault или цикл foreach, как уже упоминалось. Следует избегать ручной выборки перечислителя и вызова Current. foreach будет распоряжаться вашим перечислителем, если он реализует IDisposable. При вызове MoveNext и Current вы должны утилизировать его вручную (если применимо).
Если ваш 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;
}
Вы также можете попробовать более общую версию, которая дает вам элемент ih
enumerable.ElementAtOrDefault (я));
Надеюсь, поможет