Odpowiedzi:
Jeśli możesz używać LINQ, możesz użyć:
var e = enumerable.First();
Spowoduje to jednak zgłoszenie wyjątku, jeśli enumerable jest puste: w takim przypadku możesz użyć:
var e = enumerable.FirstOrDefault();
FirstOrDefault()zwróci, default(T)jeśli wyliczalny jest pusty, co będzie nulldotyczyło typów referencyjnych lub domyślnej wartości zerowej dla typów wartości.
Jeśli nie możesz użyć LINQ, Twoje podejście jest poprawne technicznie i nie różni się od tworzenia modułu wyliczającego przy użyciu metod GetEnumeratori w MoveNextcelu pobrania pierwszego wyniku (w tym przykładzie założono, że wyliczalne jest IEnumerable<Elem>):
Elem e = myDefault;
using (IEnumerator<Elem> enumer = enumerable.GetEnumerator()) {
if (enumer.MoveNext()) e = enumer.Current;
}
Joel Coehoorn wspomniany .Single()w komentarzach; zadziała również, jeśli spodziewasz się, że wyliczalny element zawiera dokładnie jeden element - jednak zgłosi wyjątek, jeśli jest pusty lub większy niż jeden element. Istnieje odpowiednia SingleOrDefault()metoda, która obejmuje ten scenariusz w podobny sposób jak FirstOrDefault(). Jednak David B wyjaśnia, że SingleOrDefault()nadal może zgłosić wyjątek w przypadku, gdy wyliczalny element zawiera więcej niż jeden element.
Edycja: Dziękuję Marcowi Gravellowi za wskazanie, że muszę pozbyć się mojego IEnumeratorobiektu po jego użyciu - zmodyfikowałem przykład bez LINQ, aby wyświetlić usingsłowo kluczowe w celu zaimplementowania tego wzorca.
Na wszelki wypadek, gdy używasz .NET 2.0 i nie masz dostępu do LINQ:
static T First<T>(IEnumerable<T> items)
{
using(IEnumerator<T> iter = items.GetEnumerator())
{
iter.MoveNext();
return iter.Current;
}
}
Powinno to zrobić to, czego szukasz ... używa typów ogólnych, aby uzyskać pierwszy element dowolnego typu IEnumerable.
Nazwij to tak:
List<string> items = new List<string>() { "A", "B", "C", "D", "E" };
string firstItem = First<string>(items);
Lub
int[] items = new int[] { 1, 2, 3, 4, 5 };
int firstItem = First<int>(items);
Można go łatwo zmodyfikować, aby naśladować metodę rozszerzenia 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;
}
}
Nazywając to tak:
int[] items = { 1, 2, 3, 4, 5 };
int elemIdx = 3;
int item = ElementAt<int>(items, elemIdx);
Oczywiście, jeśli zrobić mieć dostęp do LINQ, to istnieje wiele dobrych odpowiedzi już pisał ...
Cóż, nie określiłeś, której wersji .Net używasz.
Zakładając, że masz 3.5, innym sposobem jest metoda ElementAt:
var e = enumerable.ElementAt(0);
Elem e = enumerable.FirstOrDefault();
//do something with e
Użyj FirstOrDefault lub pętli foreach, jak już wspomniano. Należy unikać ręcznego pobierania modułu wyliczającego i wywoływania Current. foreach pozbędzie się modułu wyliczającego za Ciebie, jeśli implementuje IDisposable. Podczas wywoływania MoveNext i Current musisz usunąć je ręcznie (jeśli to możliwe).
Jeśli Twój IEnumerable nie ujawnia tego, <T>a Linq zawiedzie, możesz napisać metodę przy użyciu refleksji:
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;
}
możesz także wypróbować bardziej ogólną wersję, która daje i-ty element
enumerable.ElementAtOrDefault (i));
mam nadzieję, że to pomoże