web-dev-qa-db-fra.com

Quand utiliser ref et quand ce n'est pas nécessaire en C #

J'ai un objet qui est mon état en mémoire du programme et quelques autres fonctions de travail auxquelles je passe l'objet pour modifier l'état. Je l'ai passé par référence aux fonctions de travailleur. Cependant, je suis tombé sur la fonction suivante.

byte[] received_s = new byte[2048];
IPEndPoint tmpIpEndPoint = new IPEndPoint(IPAddress.Any, UdpPort_msg);
EndPoint remoteEP = (tmpIpEndPoint);

int sz = soUdp_msg.ReceiveFrom(received_s, ref remoteEP); 

Cela me trouble parce que received_s et remoteEP renvoient des éléments de la fonction. Pourquoi remoteEP a-t-il besoin d'une ref et received_s n'en a-t-il pas?

Je suis aussi un programmeur C, alors j’ai un problème pour obtenir des indicateurs hors de ma tête.

Edit: On dirait que les objets en C # sont des pointeurs sur l'objet sous le capot. Ainsi, lorsque vous transmettez un objet à une fonction, vous pouvez ensuite modifier le contenu de l'objet via le pointeur. La seule chose transmise à la fonction est le pointeur sur l'objet, de sorte que l'objet lui-même n'est pas copié. Vous utilisez ref ou out si vous voulez pouvoir changer de sujet ou créer un nouvel objet dans la fonction qui ressemble à un double pointeur. 

94
Rex Logan

Réponse courte: lisez mon article sur l'argument en passant .

Réponse longue: lorsqu'un paramètre de type référence est passé par valeur, seule la référence est passée, not une copie de l'objet. Cela revient à passer un pointeur (par valeur) en C ou C++. Les modifications apportées à la valeur du paramètre lui-même ne seront pas vues par l'appelant, mais les modifications de l'objet pointé par la référence sur sera seront visibles.

Lorsqu'un paramètre (de tout type) est passé par référence, cela signifie que tous les changements apportés au paramètre sont vus par l'appelant - les changements apportés au paramètre are changent la variable.

L'article explique tout cela plus en détail, bien sûr :)

Réponse utile: vous n’avez presque jamais besoin d’utiliser ref/out. C'est fondamentalement un moyen d'obtenir une autre valeur de retour, et devrait généralement être évité précisément parce que cela signifie que la méthode essaie probablement d'en faire trop. Ce n'est pas toujours le cas (TryParse etc. sont des exemples canoniques d'utilisation raisonnable de out), mais l'utilisation de ref/out devrait être une rareté relative.

189
Jon Skeet

Pensez à un paramètre non-ref en tant que pointeur et à un paramètre ref en tant que double pointeur. Cela m'a aidé le plus.

Vous ne devriez presque jamais passer les valeurs par réf. Je soupçonne que s’il n’y avait pas eu de problèmes d’interopérabilité, l’équipe .Net ne l’aurait jamais incluse dans les spécifications originales. La manière OO de traiter la plupart des problèmes résolus par les paramètres de référence est la suivante:

Pour plusieurs valeurs de retour

  • Créer des structures qui représentent les valeurs de retour multiples

Pour les primitives qui changent dans une méthode à la suite de l'appel de méthode (la méthode a des effets secondaires sur les paramètres des primitives)

  • Implémenter la méthode dans un objet en tant que méthode d'instance et manipuler l'état de l'objet (pas les paramètres) dans le cadre de l'appel de la méthode
  • Utilisez la solution de valeurs de retour multiples et fusionnez les valeurs de retour dans votre état
  • Créez un objet contenant un état pouvant être manipulé par une méthode et transmettez-le en tant que paramètre, mais pas les primitives elles-mêmes.
26
Michael Meadows

Vous pourriez probablement écrire une application C # entière et ne jamais transmettre d'objet/structure par réf.

J'ai eu un professeur qui m'a dit ceci: 

Le seul endroit où vous utiliseriez les arbitres est l'endroit où vous pouvez soit: 

  1. Voulez-vous passer un objet volumineux (c'est-à-dire que les objets/la structure contient des objets/structures à l'intérieur de celui-ci à plusieurs niveaux) et que le copier serait être cher et 
  2. Vous appelez un Framework, une API Windows ou une autre API nécessitant il.

Ne le faites pas simplement parce que vous le pouvez. Vous pouvez vous faire mordre dans le cul par certains Bugs méchants si vous commencez à changer les valeurs dans un paramètre et ne sont pas prêter attention.

Je suis d’accord avec lui, et depuis plus de cinq ans que j’ai étudié, je n’en avais jamais eu besoin plus que d’appeler Framework ou l’API Windows.

9
Chris

Receive_s est un tableau, vous passez un pointeur sur ce tableau. La fonction manipule les données existantes en place, sans changer l'emplacement ou le pointeur sous-jacent. Le mot-clé ref signifie que vous passez le pointeur actuel sur l'emplacement et que vous le mettez à jour dans la fonction externe. La valeur de la fonction externe est modifiée.

Par exemple. le tableau d'octets est un pointeur sur la même mémoire avant et après, la mémoire vient d'être mise à jour.

La référence de noeud final met actuellement à jour le pointeur sur le noeud final dans la fonction externe vers une nouvelle instance générée à l'intérieur de la fonction.

3
Chris Hynes

Pensez à une référence comme signifiant que vous passez un pointeur par référence. Ne pas utiliser de référence signifie que vous passez un pointeur par valeur.

Mieux encore, ignorez ce que je viens de dire (c'est probablement trompeur, en particulier avec les types de valeur) et lisez Cette page MSDN .

3
Brian

Bien que je sois d’accord avec la réponse globale de Jon Skeet et d’autres réponses, il existe un cas d’utilisation pour utiliser ref, c’est-à-dire pour renforcer les optimisations de performances. Lors du profilage des performances, il a été observé que la définition de la valeur de retour d'une méthode entraînait de légères répercussions sur les performances, alors que l'utilisation de ref en tant qu'argument selon lequel la valeur de retour est renseignée dans ce paramètre entraîne la suppression de ce goulot d'étranglement. 

Cela n’est vraiment utile que lorsque les efforts d’optimisation sont poussés à des niveaux extrêmes, au détriment de la lisibilité et éventuellement de la testabilité et de la maintenabilité pour économiser des millisecondes ou peut-être même des millisecondes.

0
Jon Davis

d'après ce que j'ai compris, tous les objets dérivés de la classe Object sont passés en tant que pointeurs, tandis que les types ordinaires (int, struct) ne le sont pas en tant que pointeurs et nécessitent une référence Je ne suis pas sûr de la chaîne (est-elle finalement dérivée de la classe Object?)

0
Lucas