web-dev-qa-db-fra.com

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 juste curieux de savoir pourquoi cela ne permet pas l'indexation.

Merci,

58
Sandeep

Parce que non.

L'indexation est couverte par IList. IEnumerable signifie "J'ai 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 ils sont accessibles article par article. IEnumerable est destiné à de telles collections. Notez qu'une collection peut implémenter à la fois IList et IEnumerable (et bien d'autres). En général, vous ne trouvez que IEnumerable comme paramètre de fonction, ce qui signifie que la fonction peut accepter tout type de collection, car tout ce dont elle a besoin est le mode d'accès le plus simple.

65
James Curran

L'interface IEnumerable<T> ne comprend pas de 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 également d'y faire référence de type IList<T>.

Sinon, vous pouvez utiliser myEnumerable.ElementAt(index) qui utilise la méthode d'extension Enumerable.ElementAt . Cela devrait fonctionner pour tous les IEnumerable<T> S. Notez qu'à moins que l'objet (au moment de l'exécution) implémente IList<T>, Cela entraînera l'énumération de tous les premiers index + 1 Éléments, tous sauf le dernier étant ignorés.

EDIT: Comme explication, IEnumerable<T> Est simplement une interface qui représente "ce qui expose un énumérateur". Une implémentation concrète peut bien être une sorte de liste en mémoire qui permet d'accéder rapidement 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 chaînée (comme mentionné par James Curran). Il peut même s'agir d'aucune sorte de structure de données en mémoire, comme un itérateur, où les éléments sont générés ("cédés") à la demande ou par un énumérateur qui récupère les éléments d'une source de données distante. Étant donné que IEnumerable<T> Doit prendre en charge tous ces cas, les indexeurs sont exclus de sa définition.

44
Ani

vous pouvez utiliser l'indexation si votre type énumérable est une chaîne comme ci-dessous

((string[])MyEnumerableStringList)[0]
3
Code_Worm

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

3
Fredrik Mörk

L'opérateur [] est résolu en propriété d'accès this[sometype index], avec une implémentation dépendant de la collection Element - .

Une Enumerable-Interface déclare un plan de ce à quoi devrait ressembler une collection en premier lieu.

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

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 aussi la 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;
     }
  }
//...
}
2
Lorenz Lo Sauer

Vous pouvez utiliser ToList pour convertir en liste. Par exemple,

SomeItems.ToList()[1]
2
Donal

L'idée des interfaces est généralement d'exposer une sorte de contrat de ligne 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 de IEnumerable<T>, ce contrat se trouve être "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 la classe Enumerable pour tonnes d'exemples.

Mais pour se concentrer sur un seul problème concret: pensez à Sum . Pour résumer un tas d'articles, de quoi avez-vous besoin? De quel contrat auriez-vous besoin? La réponse est assez simple: juste un moyen de voir chaque élément, pas plus. Un accès aléatoire n'est pas nécessaire. Même un décompte total de tous les articles n'est pas nécessaire.

Pour avoir ajouté un indexeur au IEnumerable<T> l'interface aurait été préjudiciable de deux manières:

  1. Code qui nécessite le contrat décrit ci-dessus (accès à une séquence d'éléments), s'il nécessite le IEnumerable<T> interface, serait artificiellement restrictive car elle ne pourrait pas traiter n'importe quel type qui n'implémente pas un indexeur, même si traiter un tel type devrait vraiment être bien dans les capacités du code.
  2. Tout type qui souhaitait exposer une séquence d'éléments mais n'était pas équipé de manière appropriée pour fournir un accès aléatoire par index (par exemple, LinkedList<T>, Dictionary<TKey, TValue>) devrait maintenant fournir soit des moyens inefficaces de simuler l'indexation, soit abandonner le IEnumerable<T> interface.

Tout cela étant dit, étant donné que le but d'une interface est de fournir une garantie de la fonctionnalité minimale requise dans un scénario donné, je pense vraiment que le IList<T> l'interface est mal conçue. Ou plutôt, le manque de ne interface "entre" IEnumerable<T> et IList<T> (accès aléatoire, mais aucune modification) est, à mon avis, une erreur malheureuse dans la BCL.

1
Dan Tao