web-dev-qa-db-fra.com

Comment puis-je obtenir le premier élément d'un IEnumerable <T> dans .net?

Je veux souvent saisir le premier élément d'un IEnumerable<T> dans .net et je n'ai pas trouvé de moyen agréable de le faire. Le meilleur que j'ai trouvé est:

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

Beurk! Alors, y a-t-il une bonne façon de faire cela?

132
TimK

Si vous pouvez utiliser LINQ, vous pouvez utiliser:

var e = enumerable.First();

Cela déclenchera une exception si enumerable est vide: dans ce cas, vous pouvez utiliser:

var e = enumerable.FirstOrDefault();

FirstOrDefault() renverra 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 "valeur zéro" pour les types de valeur.

Si vous ne pouvez pas utiliser LINQ, votre approche est techniquement correcte et ne diffère pas de la création d'un énumérateur à l'aide des méthodes GetEnumerator et MoveNext pour extraire le premier résultat (cet exemple suppose que énumérateur 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, une exception sera levée si l'élément est vide ou supérieur à un. Il existe une méthode correspondante SingleOrDefault() qui couvre ce scénario de la même manière que FirstOrDefault(). Cependant, David B explique que SingleOrDefault() peut toujours lever une exception dans le cas où l'énumérable contient plusieurs éléments.

Edit: Merci Marc Gravell pour avoir souligné que je dois disposer de mon objet IEnumerator après l’avoir utilisé - J'ai modifié l’exemple non-LINQ afin d’afficher le mot-clé using pour implémenter ce modèle.

210
Erik Forbes

Juste 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 afin que vous obteniez le premier élément de tout 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 pouvez 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;
    }
} 

L'appeler comme ça:

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

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

31
BenAlabaster

Eh bien, vous n’avez pas spécifié la version de .Net que vous utilisez. 

En supposant que vous ayez 3.5, une autre méthode est la méthode ElementAt:

var e = enumerable.ElementAt(0);
22
Adam Lassek

FirstOrDefault ?

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

Utilisez FirstOrDefault ou une boucle foreach comme déjà mentionné. Extraire manuellement un énumérateur et appeler Current devrait être évité. foreach disposera de votre enquêteur s'il met en œuvre IDisposable. Lorsque vous appelez MoveNext and Current, vous devez le supprimer manuellement (si applicable).

0
Mouk

vous pouvez également essayer la version plus générique qui vous donne le sixième élément 

enumerable.ElementAtOrDefault (i));

j'espère que ça aide

Si votre IEnumerable n'expose pas son <T> et que Linq échoue, vous pouvez écrire une méthode à l'aide de la réflexion:

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;
}
0
CZahrobsky

essaye ça

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