178 votes

Comment obtenir le premier élément d'un IEnumerable<T> en .net ?

Je veux souvent saisir le premier élément d'un fichier de type IEnumerable<T> en .net, et je n'ai pas trouvé de moyen agréable de le faire. La meilleure solution que j'ai trouvée est la suivante :

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

Beurk ! Alors, y a-t-il une façon agréable de faire ça ?

281voto

BrightUmbra Points 13844

Si vous pouvez utiliser LINQ, vous pouvez utiliser :

var e = enumerable.First();

Ceci lancera une exception si enumerable est vide : dans ce cas, vous pouvez utiliser :

var e = enumerable.FirstOrDefault();

FirstOrDefault() retournera default(T) si l'énumérable est vide, ce qui sera null pour les types de référence ou la valeur par défaut "zéro" pour les types de valeur.

Si vous ne pouvez pas utiliser LINQ, alors votre approche est techniquement correcte et n'est pas différente de la création d'un énumérateur à l'aide de la fonction GetEnumerator et MoveNext pour récupérer le premier résultat (cet exemple suppose qu'enumerable est un IEnumerable<Elem> ) :

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

Joel Coehoorn mentionné .Single() dans les commentaires ; cela fonctionnera également si vous vous attendez à ce que votre énumérable contienne exactement un élément - cependant, il lèvera une exception s'il est vide ou plus grand qu'un élément. Il existe une méthode correspondante SingleOrDefault() qui couvre ce scénario de manière similaire à la méthode FirstOrDefault() . Cependant, David B explique que SingleOrDefault() peut encore lever une exception dans le cas où l'énumérable contient plus d'un élément.

Edit : Merci Marc Gravell pour m'avoir fait remarquer que je devais me débarrasser de ma IEnumerator après l'avoir utilisé - j'ai modifié l'exemple non-LINQ pour afficher l'objet using pour mettre en œuvre ce modèle.

2 votes

Il est intéressant de noter que SingleOrDefault renvoie 1) le seul élément s'il n'y a qu'un seul élément. 2) null s'il n'y a aucun élément. 3) une exception s'il y a plus d'un élément.

0 votes

Bien vu, David B. - J'ai ajouté une note.

39voto

BenAlabaster Points 20189

Au cas où vous utiliseriez .NET 2.0 et n'auriez pas accès à LINQ :

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

Cela devrait faire ce que vous cherchez... il utilise des génériques pour que vous puissiez obtenir le premier élément de n'importe quel type IEnumerable.

Appelez ça comme ça :

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

Ou

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

Vous pourriez le modifier assez facilement pour imiter la méthode d'extension IEnumerable.ElementAt() de .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;
    }
} 

Je l'appelle comme ça :

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

Bien sûr, si vous faire ont accès à LINQ, alors il y a beaucoup de bonnes réponses déjà postées...

0 votes

Oups, bien sûr que vous avez raison, merci. J'ai corrigé mes exemples.

0 votes

Si vous êtes sur la version 2.0, vous n'avez pas non plus de var.

1 votes

Eh bien, je suppose que si vous voulez être pointilleux, je vais les déclarer correctement :P

33voto

Adam Lassek Points 18918

Vous n'avez pas précisé quelle version de .Net vous utilisez.

En supposant que vous disposez de la version 3.5, la méthode ElementAt est une autre solution :

var e = enumerable.ElementAt(0);

2 votes

ElementAt(0), tout simplement.

7voto

David B Points 53123

FirstOrDefault ?

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

0voto

Mouk Points 954

Utilisez FirstOrDefault ou une boucle foreach comme déjà mentionné. Il faut éviter de récupérer manuellement un énumérateur et d'appeler Current. foreach disposera votre énumérateur pour vous s'il implémente IDisposable. Lorsque vous appelez MoveNext et Current, vous devez le disposer manuellement (si possible).

2 votes

Qu'est-ce qui prouve que le recenseur doit être évité ? Les tests de performance sur ma machine indiquent qu'il a un gain de performance d'environ 10% par rapport à foreach.

0 votes

Cela dépend de ce que l'on énumère. Un Erumerator pourrait fermer la connexion à la base de données, fush et fermer le handle du fichier, libérer certains objets verrouillés, etc. Ne pas disposer d'un énumérateur d'une liste d'entiers ne sera pas nuisible, je suppose.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X