Pourquoi utiliser Enumerable.ElementAt () par rapport à l’opérateur ?

Cela semble être une question stupide, mais je n’ai pas trouvé la réponse, alors la voici. 🙂

Dans les deux cas, vous obtiendrez une exception “hors limites” si vous ne parvenez pas à vérifier les limites de votre collection. Est-ce juste une préférence de style de codage?

Et au cas où quelqu’un aurait besoin d’un exemple:

List myList = new List(){0x01, 0x02, 0x03}; byte testByte = myList.ElementAt(2); 

contre

 byte testByte = myList[2]; 

Parce que Enumerable est plus générique et qu’une collection représentée par enumerable peut ne pas avoir d’indexeur.

Mais si c’est le cas – n’utilisez pas ElementAt() cela ne va probablement pas être aussi efficace.

ElementAt() fournit une interface unifiée pour toutes les énumérations en C #. J’ai tendance à l’utiliser assez souvent car j’aime bien l’API commune.

Si le type sous-jacent prend en charge l’access aléatoire (c’est-à-dire qu’il supporte l’opérateur [] ), alors ElementAt l’utilisera. La seule surcharge est un appel de méthode supplémentaire (qui n’est presque jamais pertinent).

Si le type sous-jacent ne prend pas en charge l’access aléatoire, ElementAt() simule en itérant l’énumération jusqu’à ce qu’il arrive à l’élément qui vous intéresse. Cela peut être très coûteux et avoir des effets secondaires parfois.

Il y a aussi ElementAtOrDefault() qui est souvent très pratique.

Mise à jour : Je pensais que c’était un sujet intéressant et j’ai décidé de bloguer à ce sujet .

Fondamentalement, mon avis est qu’il est rarement logique d’utiliser une abstraction pour un access aléatoire sans exiger IList . Si vous écrivez du code qui souhaite accéder aux éléments d’une collection de manière aléatoire par index, il vous suffit d’en demander un qui vous le donne. Et si vous avez besoin d’un IList pour commencer, je ne vois vraiment pas l’intérêt d’utiliser ElementAt au lieu de l’indexeur.

Mais ce n’est que mon avis, bien sûr.


Ne l’utilisez pas pour les implémentations IList comme T[] ou List . Utilisez-le uniquement si vous en avez besoin pour les types de collecte qui ne fournissent pas un access aléatoire, tels que la Queue d’ Queue ou la Stack .

 var q = new Queue(); var s = new Stack(); for (int i = 0; i < 10; ++i) { q.Enqueue(i); s.Push(i); } // q[1] would not compile. int fromQueue = q.ElementAt(1); int fromStack = s.ElementAt(1); 

AVOID en utilisant ElementAt () dans certains scénarios !!!

Si vous savez que vous allez rechercher chaque élément, et que vous avez (ou pourriez avoir) plus de 500, appelez simplement ToArray (), stockez-le dans une variable de tableau réutilisable et indexez-le de cette façon.

Par exemple; mon code lisait les données d’un fichier Excel.
J’utilisais ElementAt () pour trouver le SharedSsortingngItem auquel ma cellule faisait référence.
Avec 500 lignes ou moins, vous ne remarquerez probablement pas la différence.
Avec 16K lignes, cela prenait 100 secondes.

Pour aggraver les choses (chaque ligne étant lue), plus l’indice grandissait et plus il fallait l’indexer à chaque itération, donc cela prenait plus de temps que prévu.
Les 1 000 premières lignes ont pris 2,5 secondes, tandis que les 1 000 dernières ont duré 10,7 secondes.

En boucle à travers cette ligne de code:

 SharedSsortingngItem ssi = sst.Elements().ElementAt(ssi_index); 

Résultat dans cette sortie enregistrée:

 ...Using ElementAt(): RowIndex: 1000 Read: 1,000 Seconds: 2.4627589 RowIndex: 2000 Read: 1,000 Seconds: 2.9460492 RowIndex: 3000 Read: 1,000 Seconds: 3.1014865 RowIndex: 4000 Read: 1,000 Seconds: 3.76619 RowIndex: 5000 Read: 1,000 Seconds: 4.2489844 RowIndex: 6000 Read: 1,000 Seconds: 4.7678506 RowIndex: 7000 Read: 1,000 Seconds: 5.3871863 RowIndex: 8000 Read: 1,000 Seconds: 5.7997721 RowIndex: 9000 Read: 1,000 Seconds: 6.4447562 RowIndex: 10000 Read: 1,000 Seconds: 6.8978011 RowIndex: 11000 Read: 1,000 Seconds: 7.4564455 RowIndex: 12000 Read: 1,000 Seconds: 8.2510054 RowIndex: 13000 Read: 1,000 Seconds: 8.5758217 RowIndex: 14000 Read: 1,000 Seconds: 9.2953823 RowIndex: 15000 Read: 1,000 Seconds: 10.0159931 RowIndex: 16000 Read: 1,000 Seconds: 10.6884988 Total Seconds: 100.6736451 

Une fois que j’ai créé un tableau intermédiaire pour stocker les éléments de SharedSsortingngItem pour le référencer, mon temps est passé de 100 secondes à 10 secondes et gère maintenant chaque ligne dans le même laps de temps.

Cette ligne de code:

 SharedSsortingngItem[] ssia = sst == null ? null : sst.Elements().ToArray(); Console.WriteLine("ToArray():" + watch.Elapsed.TotalSeconds + " Len:" + ssia.LongCount()); 

et en boucle à travers cette ligne de code:

 SharedSsortingngItem ssi = ssia[ssi_index]; 

Résultat dans cette sortie enregistrée:

 ...Using Array[]: ToArray(): 0.0840583 Len: 33560 RowIndex: 1000 Read: 1,000 Seconds: 0.8057094 RowIndex: 2000 Read: 1,000 Seconds: 0.8183683 RowIndex: 3000 Read: 1,000 Seconds: 0.6809131 RowIndex: 4000 Read: 1,000 Seconds: 0.6530671 RowIndex: 5000 Read: 1,000 Seconds: 0.6086124 RowIndex: 6000 Read: 1,000 Seconds: 0.6232579 RowIndex: 7000 Read: 1,000 Seconds: 0.6369397 RowIndex: 8000 Read: 1,000 Seconds: 0.629919 RowIndex: 9000 Read: 1,000 Seconds: 0.633328 RowIndex: 10000 Read: 1,000 Seconds: 0.6356769 RowIndex: 11000 Read: 1,000 Seconds: 0.663076 RowIndex: 12000 Read: 1,000 Seconds: 0.6633178 RowIndex: 13000 Read: 1,000 Seconds: 0.6580743 RowIndex: 14000 Read: 1,000 Seconds: 0.6518182 RowIndex: 15000 Read: 1,000 Seconds: 0.6662199 RowIndex: 16000 Read: 1,000 Seconds: 0.6360254 Total Seconds: 10.7586264 

Comme vous pouvez le constater, la conversion en un tableau a pris une fraction de seconde pour 33 560 éléments et cela en valait la peine pour accélérer mon processus d’importation.

La seule raison d’utiliser ElementAt() sur [] , est si vous avez ou avez besoin d’un IEnumerable au lieu d’un IList .

ElementAt() tente de ElementAt() d’abord IList puis utilise l’indexeur, de sorte que les performances avec ElementAt() ne seront probablement pas significativement différentes de celles utilisées directement avec l’indexeur. Donc, même si vous avez un IList , vous voudrez peut-être considérer ElementAt() s’il y a une possibilité que vous modifiiez votre type déclaré à l’avenir.

Du sharepoint vue sémantique, ils font exactement la même chose. Quelle que soit votre utilisation, elle ne va pas changer le sens du programme.

Vous utilisez [] :

  1. si l’énumérateur est déclaré comme un IList , car:

    une. c’est plus lisible, idiomatique et largement compris. No.1 raison pour moi de l’utiliser.

    b. vous voulez optimiser chaque pouce. [] devrait être légèrement plus performant car il s’agit d’une recherche directe et évite une ElementAt ( notez que ElementAt utilise l’indexeur si IEnumerable est un IList , donc ce n’est pas un gros gain ).

    c. vous êtes toujours dans l’ère de .NET 3.5.

Vous utilisez ElementAt :

  1. si le type déclaré est simplement un IEnumerable .

  2. si ElementAt rend plus lisible. Par exemple, dans les appels de style courant:

     var x = GetThatList().ElementAt(1).Wash().Spin().Rinse().Dry(); //is more readable than var x = GetThatList()[1].Wash().Spin().Rinse().Dry(); 
  3. si vous êtes vraiment résolu à utiliser un style cohérent pour IList et IEnumerable .

En termes simples, si vous avez la variable déclarée comme IList utilisez [] , sinon utilisez ElementAt ( ElementAt sur le point “déclaré” car même si votre IEnumerable est un IList dessous, vous ne pouvez utiliser que ElementAt . Casting à IList pour utiliser l’indexeur est un non non (cela arrive de toute façon dans ElementAt ) ). La lisibilité sortingomphe. Si vous avez cela à l’esprit, c’est simple à choisir. Le style d’indexeur est ce que je dois utiliser dans la grande majorité des cas. Je suis obligé d’utiliser très rarement ElementAt .

IEnumerable ne prend pas en charge l’access direct à l’index, si vous savez que vous avez une liste, continuez à utiliser le deuxième formulaire, car il est beaucoup plus facile à lire (sans doute).

ElementAt() est pris en charge pour toutes les énumérations, pas seulement pour les listes – en ElementAt() performances, elles sont identiques lorsqu’elles sont utilisées sur un type IList (une liste générique), mais si vous utilisez une liste, l’index est plus expressif. Si vous regardez la source de ElementAt() avec le réflecteur, vous verrez qu’il utilisera en interne l’access à l’index si IEnumerable est de type IList :

 .. IList list = source as IList; if (list != null) { return list[index]; } ..