Was ist der Unterschied zwischen ref und out in der Laufzeit?

15

C # stellt das Schlüsselwort refund bereit out, um Argumente als Referenz zu übergeben. Die Semantik der beiden ist sehr ähnlich. Der einzige Unterschied besteht in der Initialisierung der Flaged Variable:

  • referfordert, dass die Variable initialisiert wird, bevor sie an die Funktion übergeben wird, outnicht.
  • outerfordert, dass die Variable innerhalb der Funktion initialisiert wird, refnicht.

Die Verwendungsfälle dieser beiden Schlüsselwörter sind auch fast gleich, und ihre zu häufige Verwendung wird meines Erachtens als Codegeruch angesehen (obwohl es gültige Verwendungsfälle wie die TryParseund TryGetValueMuster gibt).

Könnte aus diesem Grund jemand erklären, warum es in C # zwei sehr ähnliche Tools für so enge Anwendungsfälle gibt?

Unter MSDN wird außerdem angegeben, dass sie ein unterschiedliches Laufzeitverhalten aufweisen:

Obwohl die Schlüsselwörter ref und out ein unterschiedliches Laufzeitverhalten verursachen, werden sie beim Kompilieren nicht als Teil der Methodensignatur betrachtet.

Wie unterscheidet sich ihr Laufzeitverhalten?

Fazit

Beide Antworten sehen richtig aus, danke euch beiden. Ich habe die von jmoreno akzeptiert , weil sie expliziter ist.

Gábor Angyal
quelle
Ich vermute, beide werden auf die gleiche Weise wie refin C ++ implementiert . auf Zeiger auf den fraglichen Objektzeiger (oder das fragliche Grundelement). dh Int32.TryParse(myStr, out myInt)(C #) wird auf die gleiche Weise wie int32_tryParse(myStr, &myInt)(C) "ausgeführt" ; Der einzige Unterschied sind einige Einschränkungen, die vom Compiler erzwungen werden, um Fehler zu vermeiden. (Ich werde dies nicht als Antwort posten, weil ich mich vielleicht irre, wie dies hinter den Kulissen funktioniert, aber ich stelle mir das so vor [weil es Sinn macht])
Cole Johnson,

Antworten:

10

Zum Zeitpunkt der Beantwortung dieser Frage war der MSDN-Artikel auf subtile Weise falsch ( seitdem wurde er korrigiert ). Anstelle von "anderes Verhalten verursachen" sollte es "anderes Verhalten erfordern" sein.

Insbesondere erzwingt der Compiler unterschiedliche Anforderungen für die beiden Schlüsselwörter, obwohl derselbe Mechanismus (IL) zum Aktivieren des Verhaltens verwendet wird.

jmoreno
quelle
Ich verstehe also, dass der Compiler nach Dereferenzierung eines outParameters oder Nichtzuweisung eines Parameters sucht ref, aber die von ihm ausgegebene IL ist nur eine Referenz. Ist das korrekt?
Andrew sagt Reinstate Monica
Übrigens könnte man das daran erkennen, dass out Tund ref Tin anderen CLI-Sprachen wie VB.Net oder C ++ / CLI identisch gerendert werden.
ACH
@ AndrewPiliser: Richtig. Angenommen, der Code wird so oder so kompiliert (Sie versuchen nicht, eine Dereferenzierung vorzunehmen, wenn Sie dies nicht sollten), dann wäre die IL identisch.
Jmoreno
4

Hier ist ein interessanter Artikel zum Thema, der Ihre Frage beantworten könnte:

http://www.dotnetperls.com/ref

Sehenswürdigkeit:

"Der Unterschied zwischen ref und out liegt nicht in der Common Language Runtime, sondern in der C # -Sprache selbst."

aktualisieren:

Der leitende Entwickler meiner Arbeit hat gerade die Antwort von @ jmoreno bestätigt.

Dan Beaulieu
quelle
Wenn ich das richtig verstehe, gibt es keinen Unterschied auf der IL-Ebene, was bedeutet, dass sie kein unterschiedliches Laufzeitverhalten haben können. Dies würde den Aussagen von MSDN widersprechen. Interessanter Artikel trotzdem!
Gábor Angyal
@ GáborAngyal du hast zufällig keinen Link zum MSDN-Artikel, oder? Es könnte schön sein, das hier zu haben, wenn sich die Meinungen unterscheiden
Dan Beaulieu
Hier gehts: msdn.microsoft.com/en-gb/library/t3c3bfhx.aspx , aber es war in meiner Frage die ganze Zeit :)
Gábor Angyal
1
Was hat stringdas alles zu tun?
Robert Harvey
Es ist nur eine Umschreibung aus dem Artikel. Aktualisiert, um die zweite Zeile zu entfernen.
Dan Beaulieu
2

Laufzeit ? Absolut keine. Sie können eine Methode nicht mit denselben Parametern überladen, die sich nur durch das Schlüsselwort ref oder out unterscheiden.

Wenn Sie versuchen, dies zu kompilieren, wird der Kompilierungsfehler "Methode mit derselben Signatur ist bereits deklariert" angezeigt:

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

Um diese Frage zu beantworten: "... warum gibt es in C # zwei sehr ähnliche Tools für so enge Anwendungsfälle?"

Aus Sicht der Codelesbarkeit und der API besteht ein großer Unterschied. Als Benutzer der API weiß ich, dass die API bei Verwendung von "out" nicht vom out-Parameter abhängt. Als API-Entwickler bevorzuge ich das "out" und benutze "ref" nur, wenn es absolut (SELTEN!) Notwendig ist. Siehe diese Referenz für eine großartige Diskussion:

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

Hintergrundinformation: Ich habe die folgende Methode kompiliert und zerlegt. Ich habe die Schlüsselwörter ref und out verwendet (in diesem Beispiel out), aber der Assemblycode hat sich nicht geändert, außer bei einem Adressverweis, wie ich es erwartet hätte:

    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 call 6E6B601E
00000025
nop param = 0;
00000026 mov eax, dword ptr [ebp-40h]
00000029 xor edx, edx
0000002b mov dword ptr [eax], edx
}
0000002d nop
0000002e lea esp, [ebp-0Ch]
00000031 pop ebx
00000032 pop esi
00000033 pop edi
00000034 pop ebp
00000035 ret

Lese ich die Versammlung richtig?

Shmoken
quelle
Was Sie schreiben, ist korrekt, aber beachten Sie, dass es keine offensichtlichen Implikationen dafür gibt, dass es keinen Laufzeitunterschied gibt, da diese Überladung nicht zulässig ist.
Gábor Angyal
Ahh - guter Punkt! Kann jemand den obigen Code zerlegen und mir Recht oder Unrecht beweisen? Ich bin schrecklich im Lesen von Versammlungen, aber neugierig.
Shmoken
Okay - Ich habe den Code mit "out" und "ref" zerlegt und sehe keinen Unterschied für meine DoSomething-Methode:
Shmoken