Quelle est la différence entre ref et out en runtime?

15

C # fournit le refet le outmot - clé pour faire passer les arguments par référence. La sémantique des deux est très similaire. La seule différence réside dans l'initialisation de la variable flaged:

  • refnécessite que la variable soit initialisée avant de la passer à la fonction, outne le fait pas.
  • outnécessite que la variable soit initialisée à l'intérieur de la fonction, refnon.

Les cas d'utilisation de ces deux mots clés sont également presque les mêmes, et leur utilisation trop fréquente est, je crois, considérée comme une odeur de code (bien qu'il existe des cas d'utilisation valides comme les modèles TryParseet TryGetValue).

Pour cette raison, quelqu'un pourrait-il expliquer pourquoi il existe deux outils très similaires en C # pour des cas d'utilisation aussi étroits?

En outre, sur MSDN , il est indiqué qu'ils ont un comportement d'exécution différent:

Bien que les mots clés ref et out provoquent des comportements d'exécution différents, ils ne sont pas considérés comme faisant partie de la signature de la méthode au moment de la compilation.

En quoi leur comportement à l'exécution est-il différent?

Conclusion

Les deux réponses semblent correctes, merci à vous deux. J'ai accepté les jmoreno parce que c'est plus explicite.

Gábor Angyal
la source
Je suppose que les deux sont implémentés de la même manière qu'en refC ++; vers des pointeurs vers le pointeur objet (ou primitif) en question. c'est-à-dire que Int32.TryParse(myStr, out myInt)(C #) est "exécuté" de la même manière que int32_tryParse(myStr, &myInt)(C); la seule différence étant certaines contraintes imposées par le compilateur pour éviter les bugs. (Je ne posterai pas cela comme réponse parce que je peux me tromper sur la façon dont cela fonctionne dans les coulisses, mais c'est ainsi que j'envisage que cela fonctionne [parce que cela a du sens])
Cole Johnson

Réponses:

10

Au moment où cette question a été posée, l'article MSDN était subtilement incorrect (il a depuis été corrigé ). Au lieu de "provoquer un comportement différent", cela devrait être "nécessite un comportement différent".

En particulier, le compilateur applique des exigences différentes pour les deux mots clés, même si le même mécanisme (IL) est utilisé pour activer le comportement.

jmoreno
la source
Donc, je comprends que le compilateur vérifie des choses comme le déréférencement d'un outparamètre ou non l'attribution d'un refparamètre, mais l'IL qu'il émet n'est qu'une référence dans les deux cas. Est-ce exact?
Andrew
Btw, on pourrait le dire du fait que out Tet ref Tsont rendus identiques dans d'autres langages CLI tels que VB.Net ou C ++ / CLI.
ach
@AndrewPiliser: correct. En supposant que le code compile dans les deux sens (vous n'essayez pas de déréférencer quand vous ne devriez pas), l'IL serait identique.
jmoreno
4

voici un article intéressant sur le sujet qui pourrait répondre à votre question:

http://www.dotnetperls.com/ref

point d'intérêt:

"La différence entre ref et out n'est pas dans le Common Language Runtime, mais dans le langage C # lui-même."

mise à jour:

le développeur principal de mon travail vient de corroborer la réponse de @ jmoreno, alors assurez-vous de la lire !.

Dan Beaulieu
la source
Donc, si je comprends bien, il n'y a pas de différence au niveau IL, ce qui implique qu'ils ne peuvent pas avoir un comportement d'exécution différent. Cela serait en contradiction avec ce que dit MSDN. Article d'Interestin quand même!
Gábor Angyal
@ GáborAngyal, vous n'avez pas un lien vers l'article MSDN par hasard, n'est-ce pas? Il pourrait être agréable de l'avoir ici si les opinions diffèrent
Dan Beaulieu
Et voilà: msdn.microsoft.com/en-gb/library/t3c3bfhx.aspx , mais c'était toujours ma question :)
Gábor Angyal
1
Qu'est-ce que cela stringa à voir avec tout cela?
Robert Harvey
Ce n'est qu'une paraphrase de l'article. Mis à jour pour supprimer la deuxième ligne ..
Dan Beaulieu
2

Runtime ? Absolument aucun. Vous ne pouvez pas surcharger une méthode avec les mêmes paramètres qui ne diffèrent que par mot-clé ref ou out.

Essayez de compiler ceci et vous obtiendrez une erreur de compilation "La méthode avec la même signature est déjà déclarée":

    private class MyClass
    {
        private void DoSomething(out int param)
        {
        }
        private void DoSomething(ref int param)
        {
        }
    }

Pour répondre à cette question: "... pourquoi il existe deux outils très similaires en C # pour des cas d'utilisation aussi étroits?"

Du point de vue de la lisibilité du code et de l'API, il y a une énorme différence. En tant que consommateur de l'API, je sais que lorsque "out" est utilisé, l'API ne dépend pas du paramètre out. En tant que développeur d'API, je préfère "out" et n'utilise "ref" que lorsque cela est absolument (RAREMENT!) Nécessaire. Voir cette référence pour une grande discussion:

/programming/1516876/when-to-use-ref-vs-out

Informations de support: j'ai compilé la méthode suivante et l'ai démontée. J'ai utilisé les mots clés ref et out (out dans cet exemple), mais le code assembleur n'a pas changé, sauf pour une référence d'adresse comme je m'y attendais:

    private class MyClass
    {
        internal void DoSomething(out int param)
        {
            param = 0;
        }
    }

00000000 push ebp
00000001 mov ebp, esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp, 34h
00000009 xor eax, eax
0000000b mov dword ptr [ebp-10h], eax
0000000e mov dword ptr [ebp-1Ch], eax
00000011 mov dword ptr [ebp-3Ch], ecx
00000014 mov dword ptr [ebp-40h], edx
00000017 cmp dword ptr ds: [008B1710h], 0
0000001e je 00000025
00000020 appel 6E6B601E
00000025 nop
param = 0;
00000026 mov eax, dword ptr [ebp-40h]
00000029 xor edx, edx
0000002b mov dword ptr [eax], edx
}
0000002d nop 00000035 ret
0000002e lea esp, [ebp-0Ch]
00000031 pop ebx
00000032 pop esi
00000033 pop edi
00000034 pop ebp

Suis-je en train de lire correctement l'assemblage?

Shmoken
la source
Ce que vous écrivez est correct, mais veuillez noter que ce n'est pas une implication évidente qu'il n'y a pas de différence d'exécution car cette surcharge n'est pas autorisée.
Gábor Angyal, le
Ahh - bon point! Quelqu'un peut-il démonter le code ci-dessus et me prouver que j'ai tort ou raison? Je suis terrible en lecture d'assemblage, mais curieux de savoir.
Shmoken
D'accord - J'ai démonté le code en utilisant "out" et "ref" et je ne vois aucune différence pour ma méthode DoSomething:
Shmoken