83 votes

Impossible d'appliquer l'indexation avec [] à une expression de type "System.Collections.Generic.IEnumerable<>".

Y a-t-il une raison spécifique pour laquelle l'indexation n'est pas autorisée dans IEnumerable.

J'ai trouvé une solution de contournement pour le problème que j'avais, mais je suis juste curieux de savoir pourquoi il ne permet pas l'indexation.

Merci,

85voto

James Curran Points 55356

Parce que ce n'est pas le cas.

L'indexation est couverte par IList . IEnumerable signifie "Je possède certains des pouvoirs d'IList, mais pas tous".

Certaines collections (comme une liste chaînée) ne peuvent pas être indexées de manière pratique. Mais on peut y accéder élément par élément. IEnumerable est destiné aux collections de ce type. Notez qu'une collection peut implémenter à la fois IList et IEnumerable (et bien d'autres). Vous ne trouvez généralement que IEnumerable comme paramètre de fonction, ce qui signifie que la fonction peut accepter n'importe quel type de collection, car tout ce dont elle a besoin est le mode d'accès le plus simple.

0 votes

En fait, il semble que ça dépende. Certaines collections msdn.microsoft.com/en-us/library/office/ peuvent être indexés. Bien sûr, vous avez raison de dire que cela ne peut pas être supposé à partir du type, ce qui le rend encore plus grossier et dégoûtant.

0 votes

@nicolas : Dans ce cas, Names n'est pas une classe, mais une interface qui étend l'interface IEnumerable pour permettre l'indexation, tout comme IList extend IEnumerable.

0 votes

Cela doit être vrai, car dans un monde COM, on peut créer des interfaces, et leur donner des noms conventionnels, mais peut-on vraiment appeler cela une interface ?

57voto

Ani Points 59747

En IEnumerable<T> ne comprend pas d'interface indexeur vous le confondez probablement avec IList<T>

Si l'objet est vraiment un IList<T> (par exemple List<T> ou un tableau T[] ), essayez de faire en sorte que la référence à celui-ci soit de type IList<T> aussi.

Sinon, vous pouvez utiliser myEnumerable.ElementAt(index) qui utilise le Enumerable.ElementAt méthode d'extension. Cela devrait fonctionner pour tous les IEnumerable<T> s . Notez qu'à moins que l'objet (d'exécution) n'implémente IList<T> ce qui aura pour conséquence que tous les premiers index + 1 les éléments à énumérer, tous sauf le dernier étant rejetés.

EDIT : En guise d'explication, IEnumerable<T> est simplement une interface qui représente "ce qui expose un énumérateur". Une implémentation concrète pourrait bien être une sorte de liste en mémoire qui fait permettre un accès rapide par index, ou non. Par exemple, il pourrait s'agir d'une collection qui ne peut pas satisfaire efficacement une telle requête, comme une liste liée (comme mentionné par James Curran). Il peut même s'agir aucune sorte de structure de données en mémoire, comme un itérateur, où les éléments sont générés à la demande, ou par un énumérateur qui va chercher les éléments dans une source de données distante. Parce que IEnumerable<T> doit supporter tous ces cas, les indexeurs sont exclus de sa définition.

0 votes

Notez que puisque ElementAt est une méthode d'extension pour IEnumerable<T>, elle doit parcourir les éléments un par un pour arriver à l'élément que vous avez demandé. Cela peut être très utile, mais peut poser un problème de performance.

1 votes

@chilltemp : J'en ai parlé.

2 votes

Il convient de noter que la mise en œuvre de l'option ElementAt est assez intelligent pour vérifier si l'objet peut être converti en IList et, si c'est le cas, l'indexer directement au lieu de faire le parcours article par article.

3voto

Fredrik Mörk Points 85694

L'une des raisons peut être que le IEnumerable peut contenir un nombre inconnu d'éléments. Certaines implémentations produisent la liste d'éléments au fur et à mesure que vous l'itérez (cf. yield pour les échantillons). Cela ne fonctionne pas très bien avec l'accès aux éléments à l'aide d'un index, ce qui nécessiterait de savoir qu'il y a au moins autant d'éléments dans la liste.

3voto

Lo Sauer Points 5469

L'opérateur []est résolu en propriété d'accès. this[sometype index] La mise en œuvre dépend de l'élément. Collection .

Une Enumerable-Interface déclare un schéma directeur de ce à quoi doit ressembler une collection en premier lieu.

Prenons cet exemple pour démontrer l'utilité de séparation propre de l'interface :

var ienu = "13;37".Split(';').Select(int.Parse);
//provides an WhereSelectArrayIterator
var inta = "13;37".Split(';').Select(int.Parse).ToArray()[0];
//>13
//inta.GetType(): System.Int32

Regardez également le syntaxe de l'opérateur []-. :

  //example
public class SomeCollection{
public SomeCollection(){}

 private bool[] bools;

  public bool this[int index] {
     get {
        if ( index < 0 || index >= bools.Length ){
           //... Out of range index Exception
        }
        return bools[index];
     }
     set {
        bools[index] = value;
     }
  }
//...
}

1 votes

Les réponses les plus votées sont peut-être vraies, mais j'ai trouvé ce commentaire plus utile dans mon propre scénario (qui ne semble pas différent du message original). Merci !

1voto

Dan Tao Points 60518

L'idée des interfaces est généralement d'exposer une sorte de contrat de base par lequel le code qui effectue un travail sur un objet peut être garanti de certaines fonctionnalités fournies par cet objet. Dans le cas des IEnumerable<T> Il se trouve que ce contrat est "vous pouvez accéder à tous mes éléments un par un".

Les types de méthodes qui peuvent être écrites sur la base de ce seul contrat sont nombreux. Voir le Enumerable classe pour tonnes d'exemples.

Mais pour ne retenir qu'un seul exemple concret : pensez à Sum . De quoi avez-vous besoin pour résumer un ensemble d'éléments ? De quel contrat auriez-vous besoin ? La réponse est très simple : un moyen de voir chaque élément, pas plus. L'accès aléatoire n'est pas nécessaire. Même un décompte total de tous les éléments n'est pas nécessaire.

Pour avoir ajouté un indexeur au IEnumerable<T> aurait été préjudiciable à deux égards :

  1. Code qui requiert le contrat décrit ci-dessus (accès à une séquence d'éléments), s'il a requis le code IEnumerable<T> serait artificiellement restrictif car il ne pourrait pas traiter un type qui n'implémente pas d'indexeur, même si le traitement d'un tel type devrait être largement dans les capacités du code.
  2. Tout type qui souhaite exposer une séquence d'éléments mais qui n'est pas équipé de manière appropriée pour fournir un accès aléatoire par index (par ex, LinkedList<T> , Dictionary<TKey, TValue> ) devrait maintenant soit fournir des moyens inefficaces de simulant ou encore d'abandonner l'indexation IEnumerable<T> l'interface.

Ceci étant dit, si l'on considère que l'objectif d'une interface est de fournir une garantie de l'intégrité de l'information. fonctionnalité minimale requise dans un scénario donné, je pense vraiment que les IList<T> est mal conçue. Ou plutôt, l'absence de une interface "entre" IEnumerable<T> y IList<T> (accès aléatoire, mais pas de modification) est, à mon avis, un oubli regrettable dans la BCL.

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