Frage

Eine nette Sache über anonyme Methoden ist, dass ich Variablen verwenden kann, die in dem anrufenden Kontext lokal sind. Gibt es einen Grund, warum dies nicht für out-Parameter und Funktionsergebnisse funktioniert?

function ReturnTwoStrings (out Str1 : String) : String;
begin
  ExecuteProcedure (procedure
                    begin
                      Str1 := 'First String';
                      Result := 'Second String';
                    end);
end;

Sehr künstliches Beispiel natürlich, aber ich lief in einige Situationen, in denen dies sinnvoll gewesen wäre.

Wenn ich versuche, dies zu kompilieren, beschwert sich der Compiler, dass er „kann Symbole nicht erfassen“. Auch ich bekam einen internen Fehler einmal, als ich versuchte, dies zu tun.

Bearbeiten Ich habe erkannt, dass es für den normalen Parameter wie

funktioniert
... (List : TList)

Ist das nicht so problematisch wie die anderen Fälle? Wer garantiert, dass die Referenz noch zeigt auf ein lebendiges Objekt aus, wenn die anonyme Methode ausgeführt wird?

War es hilfreich?

Lösung

Var und out-Parameter und die Ergebnisvariable kann nicht erfasst werden, da die Sicherheit dieser Operation nicht statisch überprüft werden kann. Wenn das Ergebnis Variable eines verwalteten Typ, wie beispielsweise eine Schnur oder eine Schnittstelle ist, wird die Speicherung tatsächlich von dem Anrufer und ein Verweis auf diesen Speicher wird als implizite Parameter übergeben zugeordnet; mit anderen Worten, dem Ergebnis variabel, je nach seiner Art, ist wie ein out-Parameter.

Die Sicherheit kann nicht aus dem Grunde, Jon erwähnt überprüft werden. Der Verschluss von einem anonymen Verfahren erstellt wird, kann die Methode der Aktivierung outlive in dem es erstellt wurde, und kann in ähnlicher Weise die Aktivierung des Verfahrens überleben, die das Verfahren aufgerufen, wo es erzeugt wurde. Daher können alle anderen var oder out-Parameter oder erfassten Ergebnis Variablen verwaiste könnte am Ende, und alle Schreibvorgänge, um sie aus dem Inneren des Verschlusses in der Zukunft verfälschen würde der Stapel.

Natürlich Delphi läuft nicht in einer verwalteten Umgebung, und es hat nicht die gleichen Sicherheitsbeschränkungen wie z.B. C #. Die Sprache könnte lassen Sie tun, was Sie wollen. Allerdings wäre es in harten führt Fehler in Situationen zu diagnostizieren, wo es schief gelaufen ist. Das schlechte Verhalten würde manifestiert sich als lokale Variablen in einem Routine-Änderungswert ohne sichtbare nächste Ursache; es wäre noch schlimmer, wenn die Methode Referenz von einem anderen Thread aufgerufen wurde.

Das wäre ziemlich schwer zu debuggen sein. Auch Hardware-Haltepunkte Speicher wäre eine relativ schlechte Sache sein, da der Stapel häufig geändert wird. Man müßte auf den Hardware-Haltepunkte drehen Speicher bedingt auf der anderen Haltepunkt trifft (zum Beispiel bei dem Methodeneintrag). Der Delphi-Debugger kann dies tun, aber ich würde eine Vermutung Gefahr, dass die meisten Menschen wissen nicht, über die Technik.

Aktualisieren : Im Hinblick auf die Ergänzungen zu Ihrer Frage, die Semantik von Wert-Instanz verweist vorbei ist wenig unterschiedlich zwischen Methoden, die einen Verschluss (und erfassen die paramete0 und Methoden enthalten, die keine a. Verschluss Jede Methode einen Verweis auf das Argument von Wert übergeben behalten können;. Methoden nicht den Parameter erfassen, um eine Liste der Referenz einfach hinzufügen können, oder speichern sie in einem privaten Bereich

Anders verhält es sich mit den Parametern als Referenz übergeben, weil die Erwartungen des Anrufers unterschiedlich sind. Ein Programmierer dies zu tun:

procedure GetSomeString(out s: string);
// ...
GetSomeString(s);

wäre sehr überrascht, wenn GetSomeString einen Verweis auf die s Variable weitergegeben halten war auf der anderen Seite.

procedure AddObject(obj: TObject);
// ...
AddObject(TObject.Create);

Es ist nicht verwunderlich, dass AddObject eine Referenz hält, da der Name impliziert, dass es den Parameter zu einem gewissen Stateful Speicher ist Zugabe. Ob der Stateful-Speicher in Form eines Verschlusses oder nicht, ist eine Implementierung Detail der AddObject Methode.

Andere Tipps

Das Problem ist, dass Ihr Str1 Variable nicht „besessen“ von ReturnTwoStrings, so dass Ihre anonymen Methode kann es nicht erfassen.

Der Grund, es nicht erfassen kann, ist, dass der Compiler nicht die ultimative Besitzer kennt (irgendwo in dem Call-Stack zu nennen ReturnTwoStrings), so kann es nicht bestimmen, wo es zu erfassen aus.

Edit: (Hinzugefügt nach einem Kommentar von Smasher )

Der Kern der anonymen Methoden ist, dass sie die Variablen erfassen (nicht ihre Werte).

Alle Bauer (CodeGear) erklärt ein bisschen mehr über variable Capturing in seinem Blog .

Es gibt eine C # Frage über Ihr Problem auch umgangen werden.

Der Out-Parameter und Rückgabewert ist irrelevant, nachdem die Funktion zurückkehrt - wie würden Sie die anonyme Methode verhalten erwarten, wenn Sie es eingefangen und später ausgeführt? (Insbesondere wenn Sie die anonyme Methode verwenden, um einen Delegierten zu schaffen, aber es nie ausführen, die Out-Parameter und Rückgabewert nicht durch die Zeit, die Funktion zurück eingestellt werden.)

Out-Parameter sind besonders schwierig - die Variable, dass die Out-Parameter Aliase Sie später nicht einmal die Delegierten durch die Zeit, rufen Sie existieren können. Beispiel: Angenommen, Sie in der Lage waren, die Out-Parameter und gibt die anonyme Methode zu erfassen, aber der Out-Parameter ist eine lokale Variable in der aufrufenden Funktion, und es ist auf dem Stapel. Wenn der Aufruf der Methode dann nach dem Speichern des Delegaten irgendwo zurück (oder Rückkehr es), was passieren würde, wenn die Delegierten schließlich genannt wurden? Wo wäre es schreiben, wenn der Wert des Out-Parameter eingestellt wurde?

Ich setze diese in einer separaten Antwort, weil Ihre EDIT macht Ihre Frage ist wirklich anders.

Ich werde wahrscheinlich diese Antwort verlängert später, als ich in einem wenig Eile bin zu einem Client zu erhalten.

Ihre Bearbeitung zeigt Sie Werttypen zu überdenken müssen, Referenztypen und die Wirkung von var, out, const und kein Parameter Markierung überhaupt.

Lassen Sie uns zunächst den Wert Arten tun.

Die Werte der Werttypen leben auf dem Stapel und haben ein Copy-on-Zuordnung Verhalten. (Ich werde versuchen, ein Beispiel dazu später enthalten).

Wenn Sie keine Parameter-Kennzeichnung, der tatsächliche Wert auf ein Verfahren (Verfahren oder Funktion) übergeben wird innerhalb der Methode auf den lokalen Wert dieses Parameters kopiert werden. So ist die Methode funktioniert nicht auf den Wert an sie übergeben, sondern auf einer Kopie.

Wenn Sie aus, var oder const, so erfolgt keine Kopie Platz: das Verfahren auf den tatsächlichen Wert übergeben beziehen. Für var, wird es ermöglichen, dass die tatsächlichen Wert zu ändern, für const wird es nicht zulassen. Denn aus, werden Sie den aktuellen Wert gelesen werden können, nicht aber noch in der Lage sein, den tatsächlichen Wert zu schreiben.

Werte von Referenztypen leben auf dem Heap, so dass für sie es kaum eine Rolle, wenn Sie aus haben, var, const oder kein Parameter Markierung: wenn Sie etwas ändern, ändern Sie den Wert auf dem Heap

.

Für Referenztypen, haben Sie immer eine Kopie erhalten, wenn Sie keinen Parameter haben Markierung, aber das ist eine Kopie einer Referenz, die auf dem Heap auf den Wert noch Punkte.

Dies ist, wo anonyme Methoden kompliziert: sie eine variable Capture tun. (Barry kann wahrscheinlich erklärt dies sogar besser, aber ich werde es versuchen) In bearbeiteten Fall wird die anonyme Methode, um die lokale Kopie der Liste zu erfassen. Die anonyme Methode wird auf dieser lokalen Kopie arbeiten, und von einer Compiler Perspektive ist alles Dandy.

der Kern Ihrer bearbeiten jedoch ist die Kombination von ‚funktioniert es für den normalen Parameter‘ und ‚wer garantiert, dass die Referenz noch zu einem lebendigen Objekt zeigt, wenn die anonyme Methode ausgeführt wird.‘

Das ist immer ein Problem mit Referenzparametern, ganz gleich, ob Sie anonyme Methoden verwenden oder nicht.

Zum Beispiel dieses:

procedure TMyClass.AddObject(Value: TObject);
begin
  FValue := Value;
end;

procedure TMyClass.DoSomething();
begin
  ShowMessage(FValue.ToString());
end;

Wer garantiert, dass, wenn jemand anruft DoSomething, dass die Instanz, wo fValue Punkte noch existiert? Die Antwort ist, dass Sie sich diese Daten nicht einfach anrufen DoSomething garantieren muss, wenn die Instanz zu fValue gestorben ist. Das gleiche gilt für Ihre bearbeiten. Sie sollten nicht die anonyme Methode aufrufen, wenn die zugrunde liegende Instanz gestorben

Dies ist einer der Bereiche, in denen Referenzlösungen gezählt oder gesammelt Müll machen das Leben leichter: (! Instanz, die dazu führen könnten, länger zu leben, als man ursprünglich erwartet) gibt die Instanz bis zum letzten Referenz am Leben gehalten wird, um es weg ist .

Also, mit Ihren bearbeiten, Ihre Frage ändert sich tatsächlich von anonymen Methoden, um die Auswirkungen der Referenz typisierte Parameter und Lebensdauer-Management im Allgemeinen verwendet wird.

Hoffentlich meine Antwort hilft Ihnen in diesem Bereich gehen.

- jeroen

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top