En C #, où utilisez-vous & # 8220; réf & # 8221; devant un paramètre?

StackOverflow https://stackoverflow.com/questions/804196

  •  03-07-2019
  •  | 
  •  

Question

Il existe déjà un certain nombre de questions sur la définition de "ref". et " out " paramètre, mais ils semblent comme mauvaise conception. Y a-t-il des cas où vous pensez que l'arbitrage est la bonne solution?

Il semble que vous puissiez toujours faire autre chose de plus propre. Quelqu'un peut-il me donner un exemple où ce serait le "meilleur"? solution à un problème?

Était-ce utile?

La solution

À mon avis, ref a largement compensé la difficulté de déclarer de nouveaux types d'utilité et la difficulté de "claquer des informations sur". aux informations existantes, des éléments sur lesquels C # a pris d’énormes progrès depuis sa genèse via LINQ, les génériques et les types anonymes.

Donc non, je ne pense pas qu'il y ait beaucoup de cas d'utilisation clairs pour cela. Je pense que c'est en grande partie un vestige de la façon dont le langage a été conçu à l'origine.

Je pense que cela a toujours du sens (comme mentionné ci-dessus) dans le cas où vous devez renvoyer une sorte de code d'erreur d'une fonction ainsi qu'une valeur de retour, mais rien d'autre (donc un type plus gros vraiment justifié.) Si je faisais cela partout dans un projet, je définirais probablement un type de wrapper générique pour le code plus-erreur-code, mais dans un cas donné, ref et out sont OK.

Autres conseils

Eh bien, ref est généralement utilisé pour des cas spécialisés, mais je n’appellerais pas cela redondant ni une fonctionnalité héritée du C #. Vous verrez qu'il (et out ) a beaucoup utilisé dans XNA par exemple. Dans XNA, une Matrix est une struct et plutôt massive (je crois que 64 octets) et il est généralement préférable de la passer à des fonctions utilisant ref pour éviter de copier 64 octets, mais seulement 4 ou 8. Une fonctionnalité C # spécialisée? Certainement. Pas plus d'utilité ou révélateur d'un mauvais design? Je ne suis pas d'accord

L'un des domaines concerne l'utilisation de petites fonctions utilitaires, telles que:

void Swap<T>(ref T a, ref T b) { T tmp = a; a = b; b = tmp; }  

Je ne vois pas d’alternative «plus propre» ici. Certes, ce n'est pas exactement le niveau d'architecture.

P / Invoke est le seul endroit où je peux vraiment penser à un endroit où vous devez utiliser les références ou les sorties. Dans d’autres cas, ils peuvent être pratiques, mais comme vous l’avez dit, il existe généralement un autre moyen, plus propre.

Et si vous vouliez renvoyer plusieurs objets , ceux-ci, pour une raison inconnue, ne sont pas liés ensemble en un seul objet.

void GetXYZ( ref object x, ref object y, ref object z);

EDIT: ??divo suggéré d'utiliser des paramètres OUT serait plus approprié pour cela. Je dois admettre qu'il a marqué un point. Je laisserai cette réponse ici comme une solution pour le compte rendu. OUT l'emporte sur REF dans ce cas.

Je pense que les meilleures utilisations sont celles que vous voyez habituellement; vous devez avoir à la fois une valeur et un "indicateur de réussite". ce n'est pas une exception d'une fonction.

Un modèle de conception dans lequel ref est utile est un visiteur bidirectionnel.

Supposons que vous disposiez d'une classe Storage pouvant être utilisée pour charger ou enregistrer les valeurs de divers types primitifs. Il est en mode Charger ou en mode Enregistrer . Il contient un groupe de méthodes surchargées appelé Transfer , et voici un exemple pour traiter les valeurs int .

public void Transfer(ref int value)
{
    if (Loading)
        value = ReadInt();
    else
        WriteInt(value);
}

Il y aurait des méthodes similaires pour d'autres types primitifs - bool , chaîne , etc.

Ensuite, sur une classe devant être "transférable", vous écririez une méthode comme celle-ci:

public void TransferViaStorage(Storage s)
{
    s.Transfer(ref _firstName);
    s.Transfer(ref _lastName);
    s.Transfer(ref _salary);
}

Cette même méthode peut soit charger les champs à partir du Stockage , soit enregistrer les champs dans le Stockage , selon le mode utilisé pour le Stockage . l'objet est en.

Vraiment, vous ne faites que répertorier tous les champs à transférer. Vous vous approchez donc de la programmation déclarative au lieu de l'impératif. Cela signifie que vous n'avez pas besoin d'écrire deux fonctions (une pour lire, une pour écrire) et que la conception que j'utilise ici dépend de l'ordre, il est donc très pratique de savoir avec certitude que les champs seront toujours lus. / écrit dans un ordre identique.

Le problème général est que lorsqu'un paramètre est marqué comme ref , vous ne savez pas si la méthode le lira ou l'écrira, ce qui vous permettra de concevoir des classes de visiteur qui travaillez dans l’une des deux directions, appelées de manière symétrique (c’est-à-dire que la méthode visitée n’a pas besoin de savoir dans quel mode de direction la classe de visiteurs opère).

Comparaison: attributs + réflexion

Pourquoi faire cela au lieu d'attribuer les champs et d'utiliser la réflexion pour implémenter automatiquement l'équivalent de TransferViaStorage ? Parce que, parfois, la réflexion est suffisamment lente pour constituer un goulot d'étranglement (mais il faut toujours profiler pour en être sûr. Ce n'est presque jamais vrai, et les attributs sont beaucoup plus proches de l'idéal de la programmation déclarative).

L'utilisation réelle de ce réside dans la création d'une structure. Les structures en C # sont des types valeur et sont donc toujours copiées complètement lorsqu'elles sont passées par valeur. Si vous devez le transmettre par référence, par exemple pour des raisons de performances ou parce que la fonction doit modifier la variable, utilisez le mot-clé ref.

Je pourrais voir si quelqu'un a une structure avec 100 valeurs (évidemment déjà un problème), vous voudrez probablement la transmettre par référence pour empêcher la copie de 100 valeurs. Le fait de retourner cette structure importante et d’écrire sur l’ancienne valeur aurait probablement des problèmes de performances.

La raison évidente d'utiliser le " ref " mot-clé est quand vous voulez passer une variable par référence. Par exemple, transmettre un type de valeur tel que System.Int32 à une méthode et modifier sa valeur réelle. Une utilisation plus spécifique pourrait être lorsque vous souhaitez échanger deux variables.

public void Swap(ref int a, ref int b)
{
   ...
}

Principale raison d'utiliser le "& out" " mot-clé est de renvoyer plusieurs valeurs d'une méthode. Personnellement, je préfère envelopper les valeurs dans une struct ou une classe spécialisée car utiliser le paramètre out produit un code plutôt moche. Paramètres passés avec & out; out " - est comme "ref" - passé par référence.

public void DoMagic(out int a, out int b, out int c, out int d)
{
   ...
}

Il existe un cas évident où vous devez utiliser le mot clé 'ref'. Si l'objet est défini mais non créé en dehors du champ de la méthode que vous souhaitez appeler ET que la méthode que vous souhaitez appeler est supposée utiliser le 'nouveau' pour le créer, vous devez utiliser 'ref' . Par exemple, {objet a; Funct (a);} {Funct (objet o) {o = nouvel objet; o.name = " dummy " ;;} ne fera PAS quelque chose avec objet 'a' et ne s'en plaindra pas non plus lors de la compilation ou de l'exécution. Cela ne fera rien. {objet a; Funct (réf. A);}} {Funct (réf. Objet o) {o = nouvel objet (); o.name = "dummy " ;;} aura pour résultat que 'a' sera un nouvel objet portant le nom de " dummy ". Mais si le 'nouveau' a déjà été fait, alors ref n'est pas nécessaire (mais fonctionne s'il est fourni). {objet a = nouvel objet (); Funct (a);} {Funct (objet o) {o.name = "dummy " ;;}

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top