Je crée une fonction où je dois passer un objet pour qu'il puisse être modifié par la fonction. Quelle est la différence entre:
public void myFunction(ref MyClass someClass)
et
public void myFunction(out MyClass someClass)
Que dois-je utiliser et pourquoi?
MyClass
unclass
type, c'est-à-dire un type de référence. Dans ce cas, l'objet que vous passez peut être modifié par lemyFunction
même sans motref
-out
clé / .myFunction
recevra une nouvelle référence qui pointe vers le même objet, et il peut modifier ce même objet autant qu'il le souhaite. La différence queref
ferait le mot - clé serait qu'il recevraitmyFunction
la même référence au même objet. Cela ne serait important que si l'myFunction
on changeait la référence pour pointer vers un autre objet.Réponses:
ref
indique au compilateur que l'objet est initialisé avant d'entrer dans la fonction, tandis qu'ilout
indique au compilateur que l'objet sera initialisé à l'intérieur de la fonction.Donc, alors que
ref
c'est bidirectionnel,out
c'est uniquement en sortie.la source
Le
ref
modificateur signifie que:Le
out
modificateur signifie que:la source
out
, peut-il être lu du tout dans la méthode, avant d'être défini par cette méthode, s'il a été initialisé avant l'appel de la méthode? Je veux dire, la méthode appelée peut-elle lire ce que la méthode appelante lui a passé comme argument?Disons que Dom se présente dans la cabine de Peter à propos du mémo sur les rapports TPS.
Si Dom était un argument de référence, il aurait une copie imprimée du mémo.
Si Dom était un argument, il ferait imprimer à Peter une nouvelle copie du mémo pour qu'il l'emporte avec lui.
la source
Je vais essayer une explication:
Je pense que nous comprenons comment fonctionnent les types de valeur? Les types de valeurs sont (int, long, struct etc.). Lorsque vous les envoyez dans une fonction sans commande ref, il COPIE les données . Tout ce que vous faites pour ces données dans la fonction affecte uniquement la copie, pas l'original. La commande ref envoie les données ACTUELLES et toute modification affectera les données en dehors de la fonction.
Ok sur la partie déroutante, types de référence:
Permet de créer un type de référence:
Lorsque vous créez un nouvel objet , deux parties sont créées:
Maintenant, lorsque vous envoyez un objet dans une méthode sans référence, il COPIE le pointeur de référence , PAS les données. Vous avez donc maintenant ceci:
Deux références pointant vers le même objet. Si vous modifiez une propriété sur un objet en utilisant reference2, cela affectera les mêmes données pointées par reference1.
Si vous annulez reference2 ou le pointez vers de nouvelles données, cela n'affectera pas reference1 ni les données reference1 pointées.
Maintenant, que se passe-t-il lorsque vous envoyez un objet par référence à une méthode? La référence réelle à someobject est envoyée à la méthode. Vous n'avez donc plus qu'une seule référence aux données:
mais qu'est ce que ça veut dire? Cela revient exactement à envoyer un objet non par référence, sauf pour deux choses principales:
1) Lorsque vous annulez la référence à l'intérieur de la méthode, celle-ci sera annulée à l'extérieur de la méthode.
2) Vous pouvez maintenant pointer la référence vers un emplacement de données complètement différent et la référence en dehors de la fonction pointera maintenant vers le nouvel emplacement de données.
la source
ref
etout
.out
mot-clé?ref est dans et hors .
Vous devez utiliser
out
de préférence partout où cela suffit pour vos besoins.la source
en dehors:
En C #, une méthode ne peut renvoyer qu'une seule valeur. Si vous souhaitez renvoyer plusieurs valeurs, vous pouvez utiliser le mot clé out. Le modificateur out retourne comme retour par référence. La réponse la plus simple est que le mot clé «out» est utilisé pour obtenir la valeur de la méthode.
réf:
En C #, lorsque vous passez un type de valeur tel que int, float, double etc. comme argument au paramètre de méthode, il est passé par valeur. Par conséquent, si vous modifiez la valeur du paramètre, cela n'affecte pas l'argument dans l'appel de méthode. Mais si vous marquez le paramètre avec le mot clé "ref", il se reflétera dans la variable réelle.
la source
Extension du chien, exemple de chat. La deuxième méthode avec ref modifie l'objet référencé par l'appelant. D'où "Chat" !!!
la source
Puisque vous passez un type de référence (une classe), il n'est pas nécessaire d'utiliser
ref
car par défaut, seule une référence à l'objet réel est transmise et, par conséquent, vous modifiez toujours l'objet derrière la référence.Exemple:
Tant que vous passez dans une classe, vous ne devez pas l'utiliser
ref
si vous souhaitez modifier l'objet à l'intérieur de votre méthode.la source
someObject = null
pourBar
terminer l'exécution. Votre code fonctionnera correctement car seuleBar
la référence à l'instance a été annulée. Aujourd'hui , le changementBar
àBar(ref MyClass someObject)
et exécuter à nouveau - vous aurez uneNullReferenceException
raisonFoo
de » la référence à l'instance a été Nulled aussi.ref
etout
se comportent de la même manière, à l'exception des différences suivantes.ref
La variable doit être initialisée avant utilisation.out
la variable peut être utilisée sans affectationout
Le paramètre doit être traité comme une valeur non affectée par la fonction qui l'utilise. Ainsi, nous pouvons utiliser unout
paramètre initialisé dans le code appelant, mais la valeur sera perdue lors de l'exécution de la fonction.la source
Pour ceux qui apprennent par l'exemple (comme moi), voici ce que dit Anthony Kolesov .
J'ai créé quelques exemples minimaux de ref, out et d'autres pour illustrer le point. Je ne couvre pas les meilleures pratiques, juste des exemples pour comprendre les différences.
https://gist.github.com/2upmedia/6d98a57b68d849ee7091
la source
"Boulanger"
C'est parce que le premier change votre référence de chaîne pour pointer sur "Baker". La modification de la référence est possible car vous l'avez passée via le mot clé ref (=> une référence à une référence à une chaîne). Le deuxième appel obtient une copie de la référence à la chaîne.
la chaîne ressemble à une sorte de spécial au premier abord. Mais la chaîne n'est qu'une classe de référence et si vous définissez
alors s est une référence à une classe de chaîne qui contient le texte "Able"! Une autre affectation à la même variable via
ne modifie pas la chaîne d'origine, mais crée simplement une nouvelle instance et laissons s pointer vers cette instance!
Vous pouvez l'essayer avec le petit exemple de code suivant:
Qu'attendez-vous? Ce que vous obtiendrez est toujours "Able" car vous définissez simplement la référence dans s sur une autre instance tandis que s2 pointe sur l'instance d'origine.
EDIT: la chaîne est également immuable, ce qui signifie qu'il n'y a tout simplement pas de méthode ou de propriété qui modifie une instance de chaîne existante (vous pouvez essayer d'en trouver une dans la documentation mais vous n'en aurez aucune :-)). Toutes les méthodes de manipulation de chaînes retournent une nouvelle instance de chaîne! (C'est pourquoi vous obtenez souvent de meilleures performances lorsque vous utilisez la classe StringBuilder)
la source
ref signifie que la valeur du paramètre ref est déjà définie, la méthode peut la lire et la modifier. Utiliser le mot clé ref revient à dire que l'appelant est responsable de l'initialisation de la valeur du paramètre.
out indique au compilateur que l'initialisation de l'objet est la responsabilité de la fonction, la fonction doit assigner au paramètre out. Il n'est pas permis de le laisser sans affectation.
la source
Out: une instruction return peut être utilisée pour renvoyer une seule valeur d'une fonction. Cependant, à l'aide des paramètres de sortie, vous pouvez renvoyer deux valeurs à partir d'une fonction. Les paramètres de sortie sont comme des paramètres de référence, sauf qu'ils transfèrent des données hors de la méthode plutôt que dans celle-ci.
L'exemple suivant illustre cela:
réf: Un paramètre de référence est une référence à un emplacement mémoire d'une variable. Lorsque vous passez des paramètres par référence, contrairement aux paramètres de valeur, un nouvel emplacement de stockage n'est pas créé pour ces paramètres. Les paramètres de référence représentent le même emplacement de mémoire que les paramètres réels fournis à la méthode.
En C #, vous déclarez les paramètres de référence à l'aide du mot clé ref. L'exemple suivant le démontre:
la source
ref et out fonctionnent comme en passant par des références et en passant par des pointeurs comme en C ++.
Pour ref, l'argument doit être déclaré et initialisé.
Pour out, l'argument doit être déclaré mais peut ou non être initialisé
la source
out double Half_nbr
.Temps de création:
(1) Nous créons la méthode d'appel
Main()
(2) il crée un objet List (qui est un objet de type référence) et le stocke dans la variable
myList
.Pendant l'exécution:
(3) Le runtime alloue une mémoire sur la pile à # 00, suffisamment large pour stocker une adresse (# 00 =
myList
, car les noms de variables ne sont en réalité que des alias pour les emplacements de mémoire)(4) Le runtime crée un objet liste sur le tas à l'emplacement mémoire #FF (toutes ces adresses sont par exemple des sakes)
(5) Le runtime stockerait alors l'adresse de départ #FF de l'objet à # 00 (ou en mots, stocke la référence de l'objet List dans le pointeur
myList
)Retour à l'heure de création:
(6) Nous passons ensuite l'objet List en argument
myParamList
à la méthode appeléemodifyMyList
et lui assignons un nouvel objet ListPendant l'exécution:
(7) Le Runtime démarre la routine d'appel pour la méthode appelée et, dans le cadre de celle-ci, vérifie le type de paramètres.
(8) Après avoir trouvé le type de référence, il alloue une mémoire sur la pile à # 04 pour aliaser la variable de paramètre
myParamList
.(9) Il y stocke également la valeur #FF.
(10) Le runtime crée un objet liste sur le tas à l'emplacement de mémoire # 004 et remplace #FF dans # 04 par cette valeur (ou a déréférencé l'objet List d'origine et pointé vers le nouvel objet List dans cette méthode)
L'adresse dans # 00 n'est pas modifiée et conserve la référence à #FF (ou le
myList
pointeur d' origine n'est pas perturbé).Le mot clé ref est une directive du compilateur pour ignorer la génération de code d'exécution pour (8) et (9), ce qui signifie qu'il n'y aura pas d'allocation de tas pour les paramètres de méthode. Il utilisera le pointeur # 00 d'origine pour opérer sur l'objet à #FF. Si le pointeur d'origine n'est pas initialisé, le runtime s'arrête de se plaindre qu'il ne peut pas continuer car la variable n'est pas initialisée
Le mot-clé out est une directive de compilation qui est à peu près la même que ref avec une légère modification en (9) et (10). Le compilateur s'attend à ce que l'argument ne soit pas initialisé et continuera avec (8), (4) et (5) pour créer un objet sur le tas et pour stocker son adresse de départ dans la variable d'argument. Aucune erreur non initialisée ne sera levée et toute référence précédente stockée sera perdue.
la source
En plus de vous permettre de réaffecter la variable de quelqu'un d'autre à une autre instance d'une classe, de renvoyer plusieurs valeurs, etc., en utilisant
ref
ou enout
laissant quelqu'un d'autre savoir ce dont vous avez besoin et ce que vous avez l'intention de faire avec la variable qu'il fournitVous n'avez pas besoin
ref
ouout
si tout ce que vous allez faire est de modifier les choses à l' intérieur de l'MyClass
instance qui est passée dans l'argumentsomeClass
.someClass.Message = "Hello World"
si vous utilisezref
,out
ou riensomeClass = new MyClass()
intérieurmyFunction(someClass)
remplace l'objet vu par lesomeClass
dans le champ d'application de lamyFunction
méthode uniquement. La méthode appelante connaît toujours l'MyClass
instance d' origine qu'elle a créée et transmise à votre méthodeVous avez besoin
ref
ouout
si vous envisagez de remplacer lesomeClass
par un nouvel objet entier et que la méthode appelante voit votre changementsomeClass = new MyClass()
intérieurmyFunction(out someClass)
change l'objet vu par la méthode qui a appelémyFunction
D'autres programmeurs existent
Et ils veulent savoir ce que vous allez faire de leurs données. Imaginez que vous écrivez une bibliothèque qui sera utilisée par des millions de développeurs. Vous voulez qu'ils sachent ce que vous allez faire de leurs variables lorsqu'ils appellent vos méthodes
L'utilisation
ref
fait une déclaration de «Passer une variable affectée à une certaine valeur lorsque vous appelez ma méthode. Soyez conscient que je pourrais la changer complètement pour autre chose au cours de ma méthode. Ne vous attendez pas à ce que votre variable pointe vers l'ancien objet quand j'ai fini "L'utilisation
out
fait une déclaration de "Passer une variable d'espace réservé à ma méthode. Peu importe qu'elle ait une valeur ou non; le compilateur me forcera à l'attribuer à une nouvelle valeur. Je garantis absolument que l'objet pointé par votre variable avant d'appeler ma méthode, sera différente lorsque j'aurai terminéSoit dit en passant, en C # 7.2, il y a aussi un
in
modificateurEt cela empêche la méthode d'échanger l'instance passée pour une autre instance. Pensez-y comme dire à ces millions de développeurs "passez-moi votre référence de variable d'origine, et je promets de ne pas échanger vos données soigneusement conçues pour autre chose".
in
a quelques particularités, et dans certains cas, comme lorsqu'une conversion implicite peut être nécessaire pour rendre votre court-métrage compatible avec un,in int
le compilateur fera temporairement un int, élargira votre court-métrage, le passera par référence et terminera. Il peut le faire parce que vous avez déclaré que vous n'allez pas jouer avec.Microsoft l'a fait avec les
.TryParse
méthodes sur les types numériques:En signalant le paramètre comme
out
ils déclarent activement ici "nous allons définitivement changer votre valeur minutieusement conçue de 98234957 pour autre chose"Bien sûr, ils doivent le faire, pour des choses comme l'analyse des types de valeurs, car si la méthode d'analyse n'était pas autorisée à échanger le type de valeur pour autre chose, cela ne fonctionnerait pas très bien .. Mais imaginez qu'il y ait une méthode fictive dans certains bibliothèque que vous créez:
Vous pouvez voir que c'est un
out
, et vous pouvez donc savoir que si vous passez des heures à chiffrer les chiffres, créez la SomeClass parfaite:Eh bien, c'était une perte de temps, prendre toutes ces heures pour faire ce cours parfait. Il va certainement être jeté et remplacé par PoorlyNamedMethod
la source
Pour ceux qui cherchent une réponse concise.
la source
Pour illustrer les nombreuses excellentes explications, j'ai développé l'application console suivante:
AppendWorld
: Une copie deStringList
namedLiStri
est transmise. Au début de la méthode, cette copie fait référence à la liste d'origine et peut donc être utilisée pour modifier cette liste.LiStri
Référence ultérieurement un autreList<string>
objet à l'intérieur de la méthode qui n'affecte pas la liste d'origine.HalloWelt
:LiStriRef
est un alias du déjà initialiséListStringRef
. L'List<string>
objet passé est utilisé pour initialiser un nouveau,ref
était donc nécessaire.CiaoMondo
:LiStriOut
est un alias deListStringOut
et doit être initialisé.Ainsi, si une méthode modifie simplement l'objet référencé par la variable transmise, le compilateur ne vous laissera pas utiliser
out
et vous ne devriez pas l'utiliserref
car cela confondrait non pas le compilateur mais le lecteur du code. Si la méthode fait que l'argument passé référence un autre objet, utilisez-leref
pour un objet déjà initialisé etout
pour les méthodes qui doivent initialiser un nouvel objet pour l'argument passé. En plus de cela,ref
etout
agissez de même.la source
Ils sont à peu près les mêmes - la seule différence est qu'une variable que vous passez en tant que paramètre de sortie n'a pas besoin d'être initialisée, et la méthode utilisant le paramètre ref doit la définir sur quelque chose.
Les paramètres de référence sont pour les données qui pourraient être modifiées, les paramètres de sortie sont pour les données qui sont une sortie supplémentaire pour la fonction (par exemple int.TryParse) qui utilisent déjà la valeur de retour pour quelque chose.
la source
Ci-dessous, j'ai montré un exemple utilisant à la fois Ref et out . Maintenant, vous serez tous débarrassés des références et des sorties.
Dans l'exemple mentionné ci-dessous lorsque je commente // myRefObj = new myClass {Name = "ref outside called !!"}; , obtiendra une erreur indiquant "Utilisation de la variable locale non affectée 'myRefObj'" , mais il n'y a pas une telle erreur in out .
Où utiliser Ref : lorsque nous appelons une procédure avec un paramètre in et que le même paramètre sera utilisé pour stocker la sortie de ce proc.
Où utiliser Out: lorsque nous appelons une procédure sans paramètre in et le même paramètre sera utilisé pour renvoyer la valeur de ce proc. Notez également la sortie
la source
vous pouvez vérifier ce code, il vous décrira sa différence complète lorsque vous utilisez "ref", cela signifie que vous initialisez déjà cet int / chaîne
mais lorsque vous utilisez "out" cela fonctionne dans les deux conditions si u initialise cette chaîne int / ou non mais u doit initialiser cette chaîne int / dans cette fonction
la source
Ref: Le mot clé ref est utilisé pour passer un argument comme référence. Cela signifie que lorsque la valeur de ce paramètre est modifiée dans la méthode, elle est reflétée dans la méthode appelante. Un argument transmis à l'aide d'un mot clé ref doit être initialisé dans la méthode appelante avant d'être transmis à la méthode appelée.
Out: Le mot-clé out est également utilisé pour passer un argument comme le mot-clé ref, mais l'argument peut être passé sans lui attribuer de valeur. Un argument transmis à l'aide d'un mot clé out doit être initialisé dans la méthode appelée avant de revenir à la méthode appelante.
Ref et out en surcharge de méthode
Ref et out ne peuvent pas être utilisés simultanément en surcharge de méthode. Cependant, ref et out sont traités différemment au moment de l'exécution, mais ils sont traités de la même manière au moment de la compilation (CLR ne fait pas de différence entre les deux lorsqu'il a créé IL pour ref et out).
la source
Du point de vue d'une méthode qui reçoit un paramètre, la différence entre
ref
etout
est que C # exige que les méthodes écrivent dans chaqueout
paramètre avant de retourner, et ne doivent rien faire avec un tel paramètre, autre que le passer en tant queout
paramètre ou y écrire , jusqu'à ce qu'il soit transmis en tant queout
paramètre à une autre méthode ou écrit directement. Notez que certaines autres langues n'imposent pas de telles exigences; une méthode virtuelle ou d'interface déclarée en C # avec unout
paramètre peut être remplacée dans un autre langage qui n'impose aucune restriction particulière à ces paramètres.Du point de vue de l'appelant, C # supposera dans de nombreuses circonstances qu'en appelant une méthode avec un
out
paramètre, la variable transmise sera écrite sans avoir été lue en premier. Cette hypothèse peut ne pas être correcte lors de l'appel de méthodes écrites dans d'autres langues. Par exemple:Si
myDictionary
identifie uneIDictionary<TKey,TValue>
implémentation écrite dans un langage autre que C #, même si elleMyStruct s = new MyStruct(myDictionary);
ressemble à une affectation, elle peut potentiellement resters
inchangée.Notez que les constructeurs écrits en VB.NET, contrairement à ceux en C #, ne font aucune hypothèse quant à savoir si les méthodes appelées modifieront les
out
paramètres et effaceront tous les champs sans condition. Le comportement étrange mentionné ci-dessus ne se produira pas avec du code écrit entièrement en VB ou entièrement en C #, mais peut se produire lorsque le code écrit en C # appelle une méthode écrite en VB.NET.la source
Si vous souhaitez passer votre paramètre en tant que référence, vous devez l'initialiser avant de passer le paramètre à la fonction, sinon le compilateur lui-même affichera l'erreur.Mais en cas de paramètre out, vous n'avez pas besoin d'initialiser le paramètre objet avant de le passer à la Vous pouvez initialiser l'objet dans la méthode appelante elle-même.
la source
Gardez bien à l'esprit que le paramètre de référence qui est passé à l'intérieur de la fonction est directement travaillé.
Par exemple,
Cela écrira Dog, not Cat. Par conséquent, vous devez travailler directement sur someObject.
la source
Je ne suis peut-être pas si bon dans ce domaine, mais les chaînes (même si elles sont techniquement des types de référence et vivent sur le tas) sont transmises par valeur, pas par référence?
C'est pourquoi vous avez besoin de ref si vous souhaitez que les modifications existent en dehors de la portée de la fonction qui les effectue, vous ne passez pas de référence autrement.
Pour autant que je sache, vous n'avez besoin que de ref pour les types structs / value et de la chaîne elle-même, car string est un type de référence qui prétend qu'il l'est mais n'est pas un type de valeur.
Je peux me tromper complètement ici, je suis nouveau.
la source
Capitalize()
qui changerait le contenu de la chaîne en lettres majuscules. Si vous avez ensuite remplacé votre lignea = "testing";
para.Capitalize();
, votre sortie serait "BONJOUR", pas "Bonjour". L'un des avantages des types immuables est que vous pouvez faire circuler les références et ne pas vous soucier de la modification de la valeur par un autre code.