Wie weise ich durch “Referenz” einem classnfeld in c # zu?

Ich versuche zu verstehen, wie durch “Verweis” zu einem classnfeld in c # zugeordnet werden.

Ich habe folgendes Beispiel zu beachten:

public class X { public X() { string example = "X"; new Y( ref example ); new Z( ref example ); System.Diagnostics.Debug.WriteLine( example ); } } public class Y { public Y( ref string example ) { example += " (Updated By Y)"; } } public class Z { private string _Example; public Z( ref string example ) { this._Example = example; this._Example += " (Updated By Z)"; } } var x = new X(); 

Wenn der obige Code ausgeführt wird, lautet die Ausgabe:

X (aktualisiert von Y)

Und nicht:

X (Aktualisiert von Y) (Aktualisiert von Z)

Wie ich gehofft hatte.

Es scheint, dass das Zuweisen eines ref-Parameters zu einem Feld die Referenz verliert.

Gibt es eine Möglichkeit, die Referenz bei der Zuweisung zu einem Feld beizubehalten?

Vielen Dank.

Nein. Ref ist eine reine Aufrufkonvention. Sie können damit kein Feld qualifizieren. In Z wird _Example auf den Wert der übergebenen Zeichenfolgenreferenz gesetzt. Anschließend weisen Sie ihm mit + = eine neue Zeichenfolgenreferenz zu. Sie vergeben nie ein Beispiel, also hat die Ref keine Wirkung.

Die einzige Problemumgehung für das, was Sie wollen, ist ein gemeinsames veränderbares Wrapper-Objekt (ein Array oder ein hypothetischer StringWrapper), das die Referenz enthält (hier eine Zeichenfolge). Im Allgemeinen können Sie, wenn Sie dies benötigen, ein größeres veränderbares Objekt für die zu teilenden classn finden.

  public class StringWrapper { public string s; public StringWrapper(string s) { this.s = s; } public string ToString() { return s; } } public class X { public X() { StringWrapper example = new StringWrapper("X"); new Z(example) System.Diagnostics.Debug.WriteLine( example ); } } public class Z { private StringWrapper _Example; public Z( StringWrapper example ) { this._Example = example; this._Example.s += " (Updated By Z)"; } } 

Wie andere bemerkt haben, können Sie kein Feld vom Typ “ref to variable” haben. Aber nur zu wissen, dass Sie es nicht tun können, ist wahrscheinlich unbefriedigend; Sie möchten wahrscheinlich auch zuerst wissen, warum nicht, und zweitens, wie Sie diese Einschränkung umgehen können.

Der Grund dafür ist, dass es nur drei Möglichkeiten gibt:

1) Felder des ref-Typs nicht zulassen

2) Erlaube unsichere Felder vom ref-Typ

3) Verwenden Sie nicht den temporären Speicherpool für lokale Variablen (aka “der Stapel”)

Angenommen, wir erlaubten Felder des ref-Typs. Dann könntest du tun

 public ref int x; void M() { int y = 123; this.x = ref y; } 

und jetzt kann y zugegriffen werden, nachdem M abgeschlossen ist. Das heißt, entweder wir sind in Fall (2) – der Zugriff auf this.x wird abstürzen und schrecklich sterben, weil der Speicher für y nicht mehr existiert – oder wir sind in Fall (3) und das lokale y wird gespeichert der Müll gesammelt Haufen, nicht der temporäre Speicherpool.

Wir mögen die Optimierung, dass lokale Variablen im temporären Pool gespeichert werden, selbst wenn sie von ref übergeben werden, und wir hassen die Idee, dass Sie eine Zeitbombe hinterlassen könnten, die Ihr Programm zum Absturz bringen und später sterben könnte. Daher ist die erste Option: keine Ref-Felder.

Beachten Sie, dass wir für lokale Variablen, die geschlossene Variablen von anonymen functionen sind, Option (3) wählen; Diese lokalen Variablen werden nicht aus dem temporären Pool zugewiesen.

Was uns dann zur zweiten Frage bringt: Wie kommst du dazu? Wenn der Grund, warum Sie ein Ref-Feld haben möchten, darin besteht, einen Getter und Setter einer anderen Variable zu erstellen, ist das völlig legal:

 sealed class Ref { private readonly Func getter; private readonly Action setter; public Ref(Func getter, Action setter) { this.getter = getter; this.setter = setter; } public T Value { get { return getter(); } set { setter(value); } } } ... Ref x; void M() { int y = 123; x = new Ref(()=>y, z=>{y=z;}); x.Value = 456; Console.WriteLine(y); // 456 -- setting x.Value changes y. } 

Und da gehst du. y wird auf dem GC-Heap gespeichert, und x ist ein Objekt, das die Fähigkeit hat, y zu erhalten und zu setzen.

Beachten Sie, dass die CLR Ref-Locals und Ref-Methoden unterstützt, obwohl C # dies nicht tut. Vielleicht unterstützt eine hypothetische zukünftige Version von C # diese functionen; Ich habe es prototypisiert und es funktioniert gut. Dies steht jedoch nicht ganz oben auf der Prioritätenliste, daher würde ich mir keine Hoffnungen machen.

UPDATE: Das im obigen Absatz erwähnte Feature wurde schließlich für Real in C # 7 implementiert. Sie können jedoch immer noch keinen Ref in einem Feld speichern.

Sie haben vergessen, die Referenz in der Z-class zu aktualisieren:

 public class Z { private string _Example; public Z(ref string example) { example = this._Example += " (Updated By Z)"; } } 

Ausgabe: X (Aktualisiert von Y) (Aktualisiert von Z)

Zu beachten ist, dass der Operator + = für eine Zeichenfolge die Methode String.Concat () aufruft. Durch das Erstellen eines neuen Zeichenfolgenobjekts wird der Wert einer Zeichenfolge nicht aktualisiert. String-Objekte sind unveränderlich, die String-class hat keine Methoden oder Felder, mit denen Sie den Wert ändern können. Ganz anders als das Standardverhalten eines regulären Referenztyps.

Wenn Sie also eine String-Methode oder einen Operator verwenden, müssen Sie den Rückgabewert immer einer Variablen zuweisen. Dies ist eine ziemlich natürliche Syntax, Werttypen verhalten sich auf die gleiche Weise. Ihr Code wäre sehr ähnlich, wenn Sie einen Int anstelle einer Zeichenfolge verwenden würden.