8
votes

Quand utiliser chacun des [], la liste , iEnumerable ?

Je me trouve habituellement à faire quelque chose comme: xxx

et je trouve tout ce mélange de types / interface un peu déroutant et il chatouille mes antennes de problèmes de performance potentielles (que j'ignore jusqu'au droit prouvé , bien sûr).

est-ce idiomatique et approprié c # ou existe une meilleure alternative pour éviter de jeter les méthodes appropriées pour travailler avec les données?

EDIT: La question est en réalité double:

  • Quand est-il propre d'utiliser l'interface ienumerable ou une matrice ou une liste (ou tout autre type d'implémentation iEnumerable) directement (lors de l'acceptation de paramètres)?

  • Si vous devriez vous déplacer librement entre ienumerables (implémentation inconnue) et les listes et les matrices et les tableaux et les tableaux et les listes ou sont que non idiomatique (il y a de meilleurs moyens de le faire) / non performants (non performants, mais pourrait-elle Être dans certains cas) / Tout simplement laids (stimulable, illisible)?


1 commentaires

Vous ne faites rien avec index :)


7 Réponses :


0
votes

J'essaie d'éviter de sauter rapidement entre les types de données si cela peut être évité.

Ce doit être le cas que chaque situation similaire à celle que vous avez décrite est suffisamment différente de manière à empêcher une règle dogmatique sur la transformation de vos types; Cependant, il est généralement pratique de sélectionner une structure de données qui offre le meilleur possible de l'interface dont vous avez besoin sans avoir à copier des éléments inutilement dans de nouvelles structures de données.


0 commentaires

7
votes

Une bonne règle de base est de toujours utiliser iEnumerable (lors de la déclaration de vos variables / paramètres de votre méthode / méthode Types de retour / Propriétés / etc.) Sauf si vous avez une bonne raison de ne pas le faire. De loin le plus compatible avec d'autres méthodes (en particulier l'extension).


11 commentaires

Toute méthode d'extension qui fonctionne avec iEnumerable fonctionnera toujours avec list et des tableaux, car ils le mettent en œuvre. (J'accepte d'utiliser cela chaque fois que possible, mais le raisonnement ici est incorrect ...)


La FindIndex n'est-elle pas une bonne raison? Peut-être que l'on pourrait la mettre en œuvre sur l'une des méthodes d'extension pour iEnumérables?


@Vinko: Je ne serais pas converti à une liste pour cela - vous pouvez le faire avec SELECT directement et TOLIST vous oblige à itérus à l'ensemble de la collection et à copier tous les éléments ...


IEnumerable n'est pas un type de données, c'est une interface. Vous ne pouvez pas "utiliser" iEnumerable, vous ne pouvez utiliser que des choses qui "implémentent" iEnumérables, dont il y en a plusieurs (matrices de string et listes étant dans cette catégorie)


Pas seulement compatible avec les autres, mais si puissant. Vous pouvez la enchaîner, le cacher, le charger paresseux, le transformer - tous cachés derrière l'un des contrats les plus bien conçus de .NET :)


S'il pouvait y avoir un -1/2, je le ferais, car cela ne traite pas vraiment de la question. String [] et la liste sont à la fois iEnumérables, donc cela ne répond même pas à la question. Si vous pouviez modifier votre réponse, ce serait la meilleure option.


@Stargazer, ma compréhension de la question de l'OP est qu'il veut savoir quels types à utiliser / déclarer. Si c'est la question, la réponse est iEnumerable . Cependant, je ne suis pas tout à fait sûr de ce que la question de l'OP est vraiment.


@Kirk, il se demande quelles sont les différences et quand les utiliser. En disant: "Utilisez toujours iEnumerable" ignore le fait que vous ne pouvez pas déclarer une instance d'iEnumérable. Lorsque vous devez instancier un objet, une chaîne [] et une liste <> implémente iEnumerable, disant donc «utiliser iEnumerable» ne fait rien à distinguer entre les deux. Ne vous méprenez pas - quand Accepter Un objet, utilisez toujours iEnumerable, mais cela ne concerne que partie de la question.


Je modifierais votre réponse à la réponse à la place: Toujours utiliser iEnumerable pour les paramètres publics, les types de retour, les propriétés, etc., sauf lorsque vous avez besoin de .Count , dans lequel Utilisation de cas ilist . Pour les variables locales, utilisez simplement le type que vous avez réellement.


@Daniel Pryden - Qu'est-ce qui ne va pas avec le compteur () méthode d'extension? :). et "quand tu as besoin" est souvent impossible de savoir avec des valeurs de retour, car vous ne pouvez pas toujours prédire les besoins de l'appelant.


@Peter Lillevold: Quel est le problème avec la méthode d'extension .Count () ? Il a o (n) performance sur Generic iEnumerable , c'est ce qui ne va pas. Ce que j'essayais de dire était, lorsque vous écrivez une fonction qui nécessite une opération O (1) de .Count ilist < T> qu'une implémentation spécifique comme list ou t [] . Pour une valeur de retour, je dirais seulement retourner un ilist si (a) il est bon marché de le faire, et (b) il y a une attente raisonnable qu'un appelant voudrait effectuer un accès aléatoire et / ou O (1) .Count .



4
votes

Eh bien, vous avez deux pommes et une orange que vous comparez.

Les deux pommes sont la matrice et la liste.

  • Un tableau en C # est un tableau de style C contenant une collection de déchets intégrée. L'avantage de les utiliser qu'ils ont très peu de frais généraux, en supposant que vous n'avez pas besoin de déplacer des objets. La mauvaise chose est qu'elles ne sont pas aussi efficaces lorsque vous ajoutez des choses, en supprimant les choses et de changer de matrimonie, car la mémoire est mélangée.

  • Une liste est un tableau dynamique de style C # (similaire à la classe Vector <> en C ++). Il y a plus de frais généraux, mais ils sont plus efficaces lorsque vous devez déplacer beaucoup de choses, car ils ne vont pas essayer de garder l'utilisation de la mémoire contiguë.

    La meilleure comparaison que je pourrais donner est de dire que les matrices doivent figurer comme des chaînes de Stringbuilders.

    L'orange est "iEnumerable". Ce n'est pas un type de données, mais c'est plutôt une interface. Lorsqu'une classe implémente l'interface iEnumerable, elle permet à cet objet d'être utilisé dans une boucle foreach ().

    Lorsque vous retournez la liste (comme vous l'avez fait dans votre exemple), vous n'êtes pas convertir la liste sur un iEnumerable. Une liste est déjà un objet iEnumerable.

    Edit: quand convertir entre les deux:

    Cela dépend de l'application. Il y a très peu de choses qui peuvent être faites avec un tableau qui ne peut pas être fait avec une liste, alors je recommanderais généralement la liste. La meilleure chose à faire est probablement de prendre une décision de conception que vous allez utiliser l'un ou l'autre, de cette façon de ne pas avoir à basculer entre les deux. Si vous comptez sur une bibliothèque externe, résumez-le pour maintenir une utilisation cohérente.

    J'espère que cela efface un peu du brouillard.


1 commentaires

Bon point sur l'interface étant une bête différente (+1), bien que cela ne réponde vraiment pas lorsque / ce qu'il faut lancer.



1
votes

me semble que le problème est que vous n'avez pas dérangé apprendre à rechercher un tableau. Indice: Array.indexof ou Array.binaireSearch Selon si le tableau est trié.

Vous avez raison que la conversion à une liste est une mauvaise idée: il gaspille de l'espace et du temps et rend le code moins lisible. Aussi, l'opportunité aveuglément à iEnumerable souligne et empêche également complètement l'utilisation de certains algorithmes (tels que la recherche binaire).


4 commentaires

Vous ne basculez pas sur iEnumerable, c'est déjà est iEnumerable. Son polymorphisme appelé ... soupir


@Vinko: system.parray n'est pas un espace de noms, c'est une classe. @Stargazer: J'ai changé "Switch" sur "Upcast". Est-ce que cela rend plus clair pour vous? Il y a un coût de performance et de fonctionnalité pour le polymorphisme.


Beaucoup mieux, merci. Il m'aime bien que les gens soignent iéquivalents comme étant dans le même bateau que la liste. Ce sont des pommes et des oranges.


Par "commutateur", j'avais voulu modifier le type formel de paramètres et les valeurs de retour, mais "Upcast" est une description beaucoup plus précise et informative de ce qui se passe lorsque ce changement est fabriqué, il espérait donc que personne ne pensera en termes de conversion.



-2
votes

Vous avez raison d'ignorer les antennes «Problème de performance» jusqu'à ce que vous ayez réellement un problème de performance. La plupart des problèmes de performance proviennent de faire trop d'E / S ou trop de verrouillage ou de faire l'un d'entre eux qui ne s'appliquent pas à cette question.

Mon approche générale est:

  1. Utilisez T [] pour des informations de style «statique» ou «snapshot». Utilisez pour les choses où appeler .Add () n'aurait aucun sens de toute façon, et vous n'avez pas besoin de la liste des méthodes supplémentaires vous donne.
  2. Acceptez iEnumerable Si vous ne vous souciez pas vraiment de ce que vous avez donné et que vous n'avez pas besoin d'une longueur / .Count de temps constant.
  3. ne renvoie que iEnumerable lorsque vous faites des manipulations simples d'une entrée IEnumerable ou lorsque vous souhaitez utiliser spécifiquement la syntaxe de rendement pour faire votre travail paresseusement.
  4. Dans tous les autres cas, utilisez la liste . C'est juste trop flexible.

    Corollaire à # 4: N'ayez pas peur de Tolist (). Tolist () est votre ami. Il oblige le iEnumerable à évaluer correctement (utile pour quand vous empilez plusieurs clauses). N'allez pas de noix avec elle, mais n'hésitez pas à l'appeler une fois que vous avez construit votre clause complète où la clause avant de faire le pourchaîner (sinon).

    Bien sûr, il s'agit simplement d'une ligne directrice approximative. Juste, essayez de suivre le même motif dans les mêmes styles de code-code qui sautent autour de vous rendre plus difficile pour les codeurs de maintenance pour entrer dans votre état d'esprit.


1 commentaires

Vous ne parlez que du développement web. Vous seriez fou de convertir tout le temps dans une partie ou dans certains travaux algorithmiques, par exemple, ou si vous traitez de grandes listes avec des éléments dans les millions de personnes. La liste est la plus chère des trois, et aussi inutilement spécifique. Utilisez-le uniquement lorsque vous ne savez pas combien d'éléments vous avez lorsque vous peuplez le tableau. N'acceptez pas les listes comme des arguments. Si vous avez besoin d'ACECSS, les valeurs de l'argument sont arbitrairement, acceptez plutôt un ilist; sinon accepter juste un iénumérable. Sinon, vous demandez simplement de réfracter l'enfer.



8
votes

en ce qui concerne la performance ...

  • la conversion de la liste à T [] implique de copier toutes les données de la liste d'origine à un tableau nouvellement attribué.
  • la conversion de T [] à la liste implique également de copier toutes les données de la liste d'origine à une liste nouvellement attribuée.
  • Conversion de la liste ou de T [] à IEnumérable implique la coulée, qui est quelques cycles de processeur.
  • la conversion de iEnumerable à la liste implique l'inverse, qui est également quelques cycles de processeur.
  • la conversion de iEnumerable à t [] implique également de l'inverse.
  • Vous ne pouvez pas lancer d'iEnumerable à T [] ou à la liste, sauf si c'était une liste [] ou une liste de commencer respectivement. Vous pouvez utiliser les fonctions TOARRAY ou TOLIST, mais celles-ci entraîneront également une copie étant faite.
  • accéder à toutes les valeurs dans l'ordre de Démarrer à la fin dans un T [] sera optimisée dans une boucle simple, d'utiliser un pointeur simple arithmétique - ce qui en fait le plus rapide d'entre eux.
  • Accéder à toutes les valeurs dans l'ordre de Démarrer à la fin dans une liste implique une vérification de chaque itération pour vous assurer que vous n'ayez pas accès à une valeur en dehors des limites de la matrice, puis de l'accès réel de la valeur de la matrice.
  • Accédant à toutes les valeurs d'un iEnumérable implique la création d'un objet d'énumérateur, appelant la fonction suivante () qui augmente le pointeur d'index, puis appelant la propriété actuelle qui vous donne la valeur réelle et le colle dans la variable que vous avez spécifiée dans votre déclaration de forach. Généralement, ce n'est pas aussi mauvais que ça sonne.
  • Accéder à une valeur arbitraire dans un iénumérable implique de commencer au début et à l'appel ensuite () autant de fois que vous devez obtenir à cette valeur. Généralement, cela est aussi mauvais que cela sonne.

    En ce qui concerne les idiomes ...

    En général, iEnumerable est utile pour les propriétés publiques, les paramètres de fonction et souvent pour les valeurs de retour - et seulement si vous savez que vous allez utiliser les valeurs de manière séquentielle.

    Par exemple, si vous aviez une fonction PrintValues, s'il a été écrit en tant qu'écriture en tant que valeurs d'impression (liste ), il ne serait en mesure de traiter que les valeurs de la liste, de sorte que l'utilisateur devait d'abord être converti, si par exemple ils utilisaient un t []. De même avec si la fonction était imprimée (t [] valeurs). Mais si c'était des valeurs imprimées (iEnumerables ), il serait capable de traiter des listes, T [] S, des piles, des hachables, des dictionnaires, des chaînes, des ensembles, etc. - toute collection qui implémente iEnumerable, qui est pratiquement tous Collection.

    En ce qui concerne l'utilisation interne ...

    • Utilisez une liste uniquement si vous n'êtes pas sûr combien d'articles devront être dedans.
    • Utilisez un T [] Si vous savez combien d'articles devront y être, mais il faut accéder aux valeurs dans un ordre arbitraire.
    • Stick avec l'iEnumerable Si c'est ce que vous avez reçu et que vous avez juste besoin de l'utiliser séquentiellement. De nombreuses fonctions vont retourner ienumerables. Si vous avez besoin d'accéder à des valeurs d'un iNeumable dans un ordre arbitraire, utilisez Toarray ().

      Aussi, notez que la coulée est différente de l'utilisation de TOARRAY () ou de TOLIST () - ce dernier implique de copier les valeurs, ce qui est en effet une performance et une mémoire frappée si vous avez beaucoup d'éléments. Le premier est simplement de dire que "un chien est un animal, comme n'importe quel animal, il peut manger" (bowncast) ou "Cet animal se trouve être un chien, il peut donc aboyer" (Upcast). De même, toutes les listes et t [] s sont iénumérables, mais seuls certains ienumerables sont des listes ou T [] s.


1 commentaires

+1, grande comparaison. Aussi dans de nombreux cas - bien que peut-être au-delà de la question originale - c'est une option intéressante d'utiliser ilist au lieu de ienumerable , en particulier lorsque des données doivent être ajoutées et enlevé.



0
votes

Quand utiliser quoi?

Je suggérerais de retourner le type le plus spécifique et de prendre le type le plus flexible. P>

Comme ceci: P>

public int[] DoSomething(IEnumerable<int> inputs)
{
    //...
}

public List<int> DoSomethingElse(IList<int> inputs)
{
    //...
}


0 commentaires