Prenons une fonction qui renvoie deux valeurs. Nous pouvons écrire:
// Using out:
string MyFunction(string input, out int count)
// Using Tuple class:
Tuple<string, int> MyFunction(string input)
// Using struct:
MyStruct MyFunction(string input)
Laquelle est la meilleure pratique et pourquoi?
struct
commeEric
mentionné.Réponses:
Ils ont chacun leurs avantages et leurs inconvénients.
Les paramètres de sortie sont rapides et bon marché, mais nécessitent que vous passiez une variable et reposiez sur une mutation. Il est presque impossible d'utiliser correctement un paramètre out avec LINQ.
Les tuples exercent une pression sur le ramassage des ordures et ne sont pas auto-documentés. «Item1» n'est pas très descriptif.
Les structures personnalisées peuvent être lentes à copier si elles sont volumineuses, mais sont auto-documentées et efficaces si elles sont petites. Cependant, il est également difficile de définir tout un tas de structures personnalisées pour des utilisations triviales.
Je serais enclin à la solution de structure personnalisée toutes choses étant égales par ailleurs. Encore mieux, c'est de créer une fonction qui ne renvoie qu'une seule valeur . Pourquoi renvoyez-vous deux valeurs en premier lieu?
MISE À JOUR: Notez que les tuples en C # 7, qui ont été livrés six ans après la rédaction de cet article, sont des types de valeur et donc moins susceptibles de créer une pression de collecte.
la source
En plus des réponses précédentes, C # 7 apporte des tuples de type valeur, contrairement à ce
System.Tuple
qui est un type de référence et offre également une sémantique améliorée.Vous pouvez toujours les laisser sans nom et utiliser la
.Item*
syntaxe:(string, string, int) getPerson() { return ("John", "Doe", 42); } var person = getPerson(); person.Item1; //John person.Item2; //Doe person.Item3; //42
Mais ce qui est vraiment puissant dans cette nouvelle fonctionnalité, c'est la possibilité d'avoir des tuples nommés. Nous pourrions donc réécrire ce qui précède comme ceci:
(string FirstName, string LastName, int Age) getPerson() { return ("John", "Doe", 42); } var person = getPerson(); person.FirstName; //John person.LastName; //Doe person.Age; //42
La destruction est également prise en charge:
(string firstName, string lastName, int age) = getPerson()
la source
Je pense que la réponse dépend de la sémantique de ce que fait la fonction et de la relation entre les deux valeurs.
Par exemple, les
TryParse
méthodes prennent unout
paramètre pour accepter la valeur analysée et renvoient unbool
pour indiquer si l'analyse a réussi ou non. Les deux valeurs n'appartiennent pas vraiment ensemble, donc, sémantiquement, cela a plus de sens, et l'intention du code est plus facile à lire, d'utiliser leout
paramètre.Si, cependant, votre fonction renvoie les coordonnées X / Y d'un objet à l'écran, alors les deux valeurs appartiennent sémantiquement ensemble et il serait préférable d'utiliser a
struct
.J'éviterais personnellement d'utiliser un
tuple
pour tout ce qui sera visible pour le code externe en raison de la syntaxe maladroite pour récupérer les membres.la source
out
paramètrenull
. Il existe quelques types immuables nullables.try
modèle qui fonctionne avec les interfaces covariantes estT TryGetValue(whatever, out bool success)
; cette approche aurait permis des interfacesIReadableMap<in TKey, out TValue> : IReadableMap<out TValue>
, et laisserait le code qui veut mapper des instances deAnimal
à des instances deCar
d'accepter unDictionary<Cat, ToyotaCar>
[usingTryGetValue<TKey>(TKey key, out bool success)
. Une telle variance n'est pas possible si elleTValue
est utilisée commeref
paramètre.J'irai avec l'approche d'utilisation du paramètre Out, car dans la deuxième approche, vous auriez besoin de créer un objet de la classe Tuple, puis d'y ajouter de la valeur, ce qui, à mon avis, est une opération coûteuse par rapport au retour de la valeur du paramètre out. Bien que si vous souhaitez renvoyer plusieurs valeurs dans Tuple Class (ce qui ne peut pas être accompli en renvoyant simplement un paramètre de sortie), je vais opter pour la deuxième approche.
la source
out
. De plus, il y a unparams
mot clé que je n'ai pas mentionné pour garder la question claire.Vous n'avez pas mentionné une autre option, qui consiste à avoir une classe personnalisée au lieu de struct. Si les données sont associées à une sémantique qui peut être exploitée par des fonctions, ou si la taille de l'instance est suffisamment grande (> 16 octets en règle générale), une classe personnalisée peut être préférée. L'utilisation de "out" n'est pas recommandée dans l'API publique en raison de son association à des pointeurs et nécessitant une compréhension du fonctionnement des types de référence.
https://msdn.microsoft.com/en-us/library/ms182131.aspx
Tuple est bon pour un usage interne, mais son utilisation est délicate dans l'API publique. Donc, mon vote est entre struct et class pour l'API publique.
la source
Il n'y a pas de «meilleure pratique». C'est ce avec quoi vous êtes à l'aise et ce qui fonctionne le mieux dans votre situation. Tant que vous êtes cohérent avec cela, aucune des solutions que vous avez publiées ne pose de problème.
la source