Also verstehe ich (glaube ich), was der in
Parametermodifikator tut. Aber was es tut, scheint ziemlich überflüssig zu sein.
Normalerweise würde ich denken, dass der einzige Grund für die Verwendung von a ref
darin besteht, die aufrufende Variable zu ändern, was von ausdrücklich verboten ist in
. Die Übergabe als in
Referenz scheint also logisch der Übergabe als Wert zu entsprechen.
Gibt es einen Leistungsvorteil? Ich war der Überzeugung, dass ein ref
Parameter auf der Back-End-Seite mindestens die physische Adresse der Variablen kopieren muss, die dieselbe Größe wie jede typische Objektreferenz haben sollte.
Gibt es dann den Vorteil nur bei größeren Strukturen, oder gibt es eine Compileroptimierung hinter den Kulissen, die es anderswo attraktiv macht? Wenn letzteres der Fall ist, warum sollte ich nicht jeden Parameter zu einem machen in
?
ref
wird verwendet, um Strukturen als Referenz zu übergeben, anstatt sie zu kopieren.in
bedeutet, dass die Struktur nicht geändert werden sollte.It means that you should never pass a non-readonly struct as in parameter.
Antworten:
in
wurde kürzlich in die C # -Sprache eingeführt.in
ist eigentlich einref readonly
. Im Allgemeinen gibt es nur einen Anwendungsfallin
, der hilfreich sein kann: Hochleistungs-Apps, die mit vielen großenreadonly struct
s umgehen.Vorausgesetzt, Sie haben:
readonly struct VeryLarge { public readonly long Value1; public readonly long Value2; public long Compute() { } // etc }
und
void Process(in VeryLarge value) { }
In diesem Fall wird die
VeryLarge
Struktur als Referenz übergeben, ohne dass defensive Kopien erstellt werden, wenn diese Struktur in derProcess
Methode verwendet wird (z. B. beim Aufrufenvalue.Compute()
), und die Unveränderlichkeit der Struktur wird vom Compiler sichergestellt.Beachten Sie, dass das Übergeben einer nicht schreibgeschützten Datei
struct
mit einemin
Modifikator dazu führt, dass der Compiler beim Aufrufen der Methoden von struct und beim Zugriff auf Eigenschaften in der obigen Methode eine defensive Kopie erstelltProcess
, was sich negativ auf die Leistung auswirkt!Es gibt einen wirklich guten MSDN-Blogeintrag, den ich sorgfältig lesen sollte.
Wenn Sie mehr über den historischen Hintergrund der
in
Einführung erfahren möchten, können Sie diese Diskussion im GitHub-Repository der C # -Sprache lesen .Im Allgemeinen sind sich die meisten Entwickler einig, dass die Einführung von
in
als Fehler angesehen werden kann. Es ist eine ziemlich exotische Sprachfunktion und kann nur in Fällen mit hoher Leistung nützlich sein.quelle
ref
, zum Beispiel , indem siesomeProc(in thing.someProperty);
gegenüberpropType myProp = thing.someProperty; someProc(ref myProp);
? ähneltin
ein C # -Nur-Konzeptout
oder wurde es dem .NET Framework hinzugefügt?in
, da siein
effektivref
über ein spezielles Attribut verfügt. Ihr erstes Snippet wird also nicht kompiliert. @VisualMelon, das ist richtig, das defensive Kopieren erfolgt beim Aufrufen von Methoden oder beim Zugriff auf Eigenschaften der Struktur aus der Methode heraus, die die Struktur als Argument erhält.in
undout
sind Konzepte, die von Anfang an im Framework enthalten sein sollten (zusammen mit einem Mittel, mit dem Methoden und Eigenschaften angeben können, ob sie sich ändernthis
). Ein Compiler könnte zulassen, dass eine Eigenschaft an einin
Argument übergeben wird, indem ein Verweis auf eine temporäre Eigenschaft übergeben wird, die sie enthält. Wenn eine in einer anderen Sprache geschriebene Funktion diese temporäre Funktion ändert, ist die Semantik etwas schwierig, aber das liegt daran, dass das Verhalten der Funktion nicht mit ihrer Signatur übereinstimmt.Richtig.
Ja.
Es gibt keine Anforderung, dass eine Referenz auf ein Objekt und eine Referenz auf eine Variable beide dieselbe Größe haben, und es gibt keine Anforderung, dass beide die Größe eines Maschinenworts haben, aber ja, in der Praxis sind beide 32 Bit auf 32 Bitmaschinen und 64-Bit auf 64-Bit-Maschinen.
Was Ihrer Meinung nach die "physische Adresse" damit zu tun hat, ist mir unklar. Unter Windows verwenden wir virtuelle Adressen , keine physischen Adressen im Benutzermoduscode. Unter welchen möglichen Umständen würden Sie sich vorstellen, dass eine physikalische Adresse in einem C # -Programm von Bedeutung ist? Ich bin neugierig zu wissen.
Es ist auch nicht erforderlich, dass eine Referenz jeglicher Art als virtuelle Adresse des Speichers implementiert wird. Referenzen könnten undurchsichtige Handles in GC-Tabellen in einer konformen Implementierung der CLI-Spezifikation sein.
Das Motivationsszenario für das Feature besteht darin, die Kosten für das Übergeben größerer Strukturen zu senken .
Beachten Sie, dass es keine Garantie gibt,
in
die ein Programm tatsächlich schneller macht, und dass es Programme langsamer machen kann. Alle Fragen zur Leistung müssen durch empirische Forschung beantwortet werden . Es gibt nur sehr wenige Optimierungen, die immer gewinnen . Dies ist keine "immer gewinnen" -Optimierung.Der Compiler und die Laufzeit dürfen jede von ihnen gewählte Optimierung vornehmen, wenn dies nicht gegen die Regeln der C # -Spezifikation verstößt. Es gibt meines Wissens noch keine solche Optimierung für
in
Parameter, aber das schließt solche Optimierungen in Zukunft nicht aus.Angenommen, Sie haben
int
stattdessen einenin int
Parameter als Parameter festgelegt. Welche Kosten fallen an?Angenommen, es ist ein
double
und Sie ändern es in einin double
. Auch hier kann die Variable jetzt nicht mehr in ein Hochleistungs-Gleitkommaregister eingetragen werden. Dies hat nicht nur Auswirkungen auf die Leistung , sondern kann auch das Programmverhalten ändern! C # darf Float-Arithmetik mit einer Genauigkeit von mehr als 64 Bit ausführen und dies normalerweise nur, wenn die Floats registriert werden können.Dies ist keine kostenlose Optimierung. Sie müssen die Leistung an den Alternativen messen. Ihre beste Wahl ist es, überhaupt keine großen Strukturen zu erstellen, wie es die Designrichtlinien vorschlagen.
quelle
in
wirklich in allen Fällen gleichbedeutend mit dem Übergeben von Wert? Wenn wir habenf(ref T x, in T y)
undf
modifizierenx
, sollte es die gleiche Änderung wiey
beim Aufrufen von beobachtenf(ref a,a)
. Gleiches gilt auch, wennf
einin T y
und ein Delegat benötigt werden, die sichy
beim Aufruf ändern . Ohnein
wäre die Semantik anders, day
sich ihr Wert meiner Meinung nach nie ändern würde, da es sich um eine Kopie handeln würde.in
ist es, den Begriff "Read by Reference, Readonly" darzustellen, der logisch dem Übergeben von Werten entspricht, wenn Sie sich vernünftig verhalten .Es gibt. Beim Übergeben von a ermöglicht
struct
dasin
Schlüsselwort eine Optimierung, bei der der Compiler nur einen Zeiger übergeben muss, ohne dass das Risiko besteht, dass die Methode den Inhalt ändert. Letzteres ist kritisch - es vermeidet einen Kopiervorgang. Bei großen Strukturen kann dies einen großen Unterschied machen.quelle
Dies geschieht aufgrund des funktionalen Programmieransatzes. Eines der Hauptprinzipien ist, dass die Funktion keine Nebenwirkungen haben sollte, was bedeutet, dass sie die Werte der Parameter nicht ändern und einen bestimmten Wert zurückgeben sollte. In C # gab es keine Möglichkeit, Strukturen (und Werttypen) zu übergeben, ohne nur durch Referenz kopiert zu werden, wodurch der Wert geändert werden kann. In Swift gibt es einen Hacky-Algorithmus, der struct kopiert (ihre Sammlungen sind übrigens structs), solange die Methode beginnt, ihre Werte zu ändern. Leute, die schnell arbeiten, sind sich nicht aller der Kopien bewusst. Dies ist eine nette C # -Funktion, da sie speichereffizient und explizit ist. Wenn Sie sich ansehen, was es Neues gibtSie werden sehen, dass immer mehr Dinge um Strukturen und Arrays im Stapel herum gemacht werden. Und in Aussage ist nur für diese Funktionen notwendig. In den anderen Antworten werden Einschränkungen erwähnt, die jedoch nicht unbedingt erforderlich sind, um zu verstehen, wohin .net führt.
quelle
in es ist nur lesbar Referenz in C # 7.2
Dies bedeutet, dass Sie nicht das gesamte Objekt an den Funktionsstapel übergeben, ähnlich wie bei ref, wenn Sie nur einen Verweis auf die Struktur übergeben
Der Versuch, den Wert des Objekts zu ändern, führt jedoch zu einem Compilerfehler.
Auf diese Weise können Sie die Codeleistung optimieren, wenn Sie große Strukturen verwenden.
quelle