10
votes

Devrais-je toujours appeler .Tearray sur les résultats de la requête LINQ renvoyés dans une fonction?

Je rencontre assez de cas de collection a été modifié; L'opération d'énumération peut ne pas exécuter les erreurs lors du retour des résultats d'une requête LINQ dans une fonction, comme celui-ci ... (Je dois ajouter la fonction active comme une implémentation d'une interface et les résultats laissent ce module à utiliser dans un autre.) xxx

devrais-je, en règle générale, appelez-moi toujours .toTarray lors du renvoi du résultat de la requête LINQ dans une fonction ou une propriété getter si les données sous-jacentes ont potentiel d'être changé? Je sais qu'il y a un peu d'efficacité de faire cela, mais j'ai le sentiment que c'est la chose sûre à faire, et devrait donc toujours être fait pour éviter les problèmes de couplage temporelle.

edit:

Permettez-moi de faire un meilleur travail expliquant le domaine du problème.

Nous avons une mise en œuvre du graphique de notre principal sujet de préoccupation, ce qui est un problème d'optimisation. Les entités sont représentées sous forme de nœuds graphiques. Les bords pondérés avec divers coûts et d'autres paramètres expressent des relations entre les nœuds. Lorsque l'utilisateur manipule les données, nous créons des bords différents et évaluons les différentes options qu'elles peuvent prendre contre l'état actuel afin de leur donner des commentaires sur les résultats de chaque option. Les modifications apportées aux données sur le serveur par d'autres utilisateurs et programmes sont immédiatement propagées au client via la technologie Push. Nous utilisons beaucoup de threading ...

... tout cela signifie que nous avons beaucoup de choses qui se produisent de manière très asynchrone.

Notre programme est divisé en modules ( Sur la base du principe de responsabilité unique) avec un projet de contrat et d'un projet de mise en œuvre de temps à exécution résolu, ce qui signifie que nous comptons beaucoup d'interfaces. Nous transmettons généralement des données entre les modules à l'aide d'iEnumérables (car ils sont type de type immuable).


2 commentaires

Pourriez-vous poster une petite version pouvant être compilée d'un programme qui le reproduit?


Malheureusement, notre base de code est comme 50000+ LOI avec 68 projets, ce qui produira un bon exemple, sera un peu difficile. Voir les détails élargis dans la question.


4 Réponses :


2
votes

Si vous allez retourner un iéenérable (ou iquéryable, ou quoi que ce soit comme ceux qui ne sont pas autonomes), les restrictions sur quand on peut appeler, que peut-on faire avec cela ou combien de temps cela puisse être tenu sur nécessaire pour être clairement énoncé.

Pour ces raisons, je recommanderais de retourner fuzzbuzz [] au lieu de iEnumerable s'il s'agit d'une API d'une sorte (c'est-à-dire entre couches). Si cela fait partie de la mise en oeuvre interne d'une classe / module, il est plus facile de justifier un iNeumérable d'évaluation retardé , mais toujours raisonnable d'utiliser la matrice.

Sauf si le nombre de résultats est important ou que cela s'appelle fréquemment, il est peu probable que ce soit une préoccupation de performance (dans de nombreux scénarios, le temps de la CPU est bon marché et la mémoire allouée au tableau ne sera pas conservée très longtemps) .


2 commentaires

Devrait être mentionné que dans certaines situations (comme la mise en œuvre de Linq de Nhibernate), la coulée implicitement iquéryable à iEnumerable fera courir la requête.


J'aime la mention que le type de retour devrait être une décision de l'API. Je pense qu'il y a trop d'entre nous qui manquent de penser à l'API que nous infligons sur nos camarades.



5
votes

En général, vous ne devriez pas TOUJOURS CALL .TORAYRAY ou .Tolist Lors du retour du résultat de la requête LINQ.

Les deux .toarray et .tolist sont des opérations "gourmandes" (opposées aux paresseuses) qui effectuent réellement une requête à la source de vos données. Et le lieu approprié et le temps de les appeler sont une décision d'architecture. Par exemple, vous pouvez établir une règle de votre projet pour matérialiser toutes les requêtes de Linq à l'intérieur de la couche d'accès aux données et gérer ainsi toute l'exception de la couche de données. Ou de ne pas être exécutés aussi longtemps que possible et ne reçoivent que les données requises à la fin. Et il y a beaucoup d'autres détails liés à ce sujet.

mais à appeler, ou à ne pas appeler .toarray Lorsque vous renvoyez le résultat de votre fonction - ce n'est pas une question et il n'a aucune réponse avant de présenter un échantillon plus détaillé.


2 commentaires

J'ai ajouté plus de détails à la question (bien que ce n'est pas un exemple de code détaillé), si vous souhaitez mettre à jour votre réponse ...


Point clé est Notre programme est divisé en modules (basé sur le principe de responsabilité unique ) - Voulez-vous ne pas mélanger et comment allez-vous diviser Accès aux données et la mise en œuvre basée sur le graphique de notre principal sujet de préoccupation ? Et cette réponse est individuelle pour chaque projet



2
votes

"En règle générale", non, vous ne devriez pas toujours appeler TOLIST / TARARRAY. Sinon, des requêtes telles que mydata.getsomesubset (). OùCondiace (). Joindre (autresData) passe un tas de temps en affectant des tampons temporaires pour chaque appel en chaîne. Mais Linq fonctionne mieux avec des collections immuables. Vous voudrez peut-être être plus prudent au point où vous modifiez mysecretdatasource .

Spécifiquement, si votre code est toujours structuré autour de la modification fréquente de votre DataSource, cela ressemble à une bonne raison pour renvoyer avec impatience un tableau au lieu d'un i


0 commentaires

5
votes

Non, je ne ferais pas de règle de cela.

Je comprends votre préoccupation. Le côté appelait peut ne pas être conscient que ses actions affectent les résultats de la requête.

Il y a quelques cas où vous ne pouvez vraiment pas faire cela:

  • Il existe des exemples où cela le ferait de la mémoire, par exemple avec des énumérables infinies, ou de l'énumérateur qui produit une image récemment calculée chaque itération. (J'ai les deux).
  • Si vous utilisez tout () ou premier () sur vos requêtes. Les deux n'ont besoin que de lire le premier élément. Tous les autres travaux sont effectués en vain.
  • Si vous attendez que Enumerables soit enchaînée avec des tuyaux / filtres. Matérialisation des résultats intermédiaires n'est qu'un coût supplémentaire.

    D'autre part, dans de nombreux cas, il est plus sûr de matérialiser la requête dans un tableau lorsque elle est concevable que l'utilisation de la matrice aura des effets secondaires qui affecteront la requête.

    Lorsque vous écrivez un logiciel, cela semble attirer des règles qui disent "lorsque vous devez choisir entre x et y, faites toujours x". Je ne crois pas qu'il y ait de telles règles. Peut-être que dans 15%, vous devriez vraiment faire x, dans 5%, vous devez absolument avoir besoin de Y, et pour le reste des cas, cela n'a tout simplement pas d'importance.

    Pour ces 80% restants, rien ne peut être la chose appropriée. Si vous insérez TOARRAY () partout, le code suggère injustement qu'il y avait une raison pour laquelle cela est fait.


1 commentaires

+1 pour "Le code suggère à tort qu'il y avait une raison pour laquelle cela se fait". Lorsqu'une ligne de code est écrite, personne ne tombera sans danger pour le supprimer. Cela entraîne souvent du code spaghetti au fil du temps.