Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream) (https://www.delphipraxis.net/180328-unterschied-zwischen-var-afilestream-tfilestream-und-afilestream-tfilestream.html)

michele_tedesco 12. Mai 2014 16:28

Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream)
 
Hallo zusammen

Irgendwie verstehe ich den Unterschied nicht, wenn ein Stream mit und ohne VAR als Parameter übergeben wird.
Nach meinem Verständnis wird mit VAR die selbe Adresse des Objekts übergeben und ohne VAR nur eine Kopie des Objektes (somit eine andere Adresse) übergeben.

Wenn ich aber im XE5 Debugger die Adresse vergleiche vom Objekt welches OHNE VAR übergeben wird und vom Parameter der aufgeruften Methode, dann sind die Adressen die selbben, auch wenn ich nicht VAR verwendet habe.


Wenn ich den selben Aufruf der Mothode mit VAR im Parameter habe, dann sind die Adressen auch die gleichen.

Was habe ich da missverstanden?

Danke im Voraus!

Dejan Vu 12. Mai 2014 16:44

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Nichts.
Änderungen am Inhalt von 'VAR'-Parametern werden nach außen mitgeteilt. Ohne 'VAR' bleibt die Veränderung des Inhaltes transparent, d.h. nichts geht nach außen.

WIE das vom Compiler umgesetzt wird, ist nebensächlich. Klar, er könnte eine Kopie übergeben, aber wenn in der Routine nie etwas verändert wird, war die Kopie überflüssig. Ergo würde vielleicht ein Referenzzähler, oder ein Lazy-Copy mehr bringen, d.h. es wird die Originaladresse übergeben, aber beim ersten Ändern wird eine Kopie angelegt.

Bei einfachen Datentypen wird bei ohne VAR der Wert direkt übergeben, mit Var die Adresse.

Einfaches Beispiel:
Delphi-Quellcode:
Procedure Foo (Bar : Integer);
Begin
  Bar := Bar + 1;
End;
Procedure FooWithVar (Var Bar : Integer);
Begin
  Bar := Bar + 1;
End;
...
  n := 1;
  Foo(n); // die '1' wird auf den Stack gepackt, und logischerweise 'n' nicht verändert
  Assert(n = 1);
  FooWithVar(n); // Die Adresse von 'n' wird auf den Stac gepackt und so kann der Inhalt von 'n' verändert werden
  Assert(n = 2);
Und für Objekte gilt: VAR nur dann, wenn die Instanz umgebogen wird.
Delphi-Quellcode:
Procedure ChangeTheStream (Var aStream : TStream);
Begin
  aStream := TMyStream.Create();
End;
...
  // Autschn. Speicherleck.
  theStream := TFileStream.Create...
  ChangeTheStream (theStream);
  Assert (theStream is TMyStream);
Das Problem beim Objekt-Parameter-VAR ist doch: Wer ist hier eigentlich zuständig für das Freigeben? Normalerweise gilt ja: Der, der die Instanz erzeugt, gibt sie auch wieder frei (produziert sauberen Code). Aber hier ist das nicht klar, wer das macht bzw. wer zuständig ist. Ergo: Finger weg, brauchste nicht.

Sir Rufo 12. Mai 2014 16:45

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Hier wird das doch erklärt? :gruebel:
http://docwiki.embarcadero.com/RADSt...meter_(Delphi)

Dejan Vu 12. Mai 2014 16:57

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Ich glaube, eine (halbwegs) ausführliche Antwort ist besser, auch zum Verständiss anderer, die hier nachlesen.
Zudem beantwortet die Erklärung im Wiki die Frage auch nicht:
Zitat:

Die meisten Parameter sind Wert- (Standard) oder Variablenparameter (var). Wertparameter werden als Wert übergeben, Variablenparameter als Referenz.
widerspricht der Beobachtung
Zitat:

Zitat von michele_tedesco (Beitrag 1258621)
...Adresse vergleiche die selben, auch wenn ich nicht VAR verwendet habe


p80286 12. Mai 2014 17:06

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Zitat:

Zitat von Sir Rufo (Beitrag 1258626)
Hier wird das doch erklärt? :gruebel:
http://docwiki.embarcadero.com/RADSt...meter_(Delphi)

Eben nicht, oder besser gesagt nur für die einfachen Typen wie Integer,Boolean usw. Da ist wohl seit TP 3 immer wieder abgeschrieben worden?

Delphi-Quellcode:
procedure Zeigwas(const wert:string ;Lb:Tlabel);
begin
  lb.caption:=wert;
end;
so bekommt das Label einen neuen Wert, ist ja klar, wenn man sich vor Augen hält, daß hier die Adresse von einem Label übergeben wird und nicht das Label oder eine neue Instanz des Labels.
Ob das alles allerdings so ganz sauber und im Sinne des Erfinders ist:gruebel:

Gruß
K-H

Sir Rufo 12. Mai 2014 17:41

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Es gibt call by value, call by reference und const parameter.

Ein kleiner Test zeigt die Unterschiede
Delphi-Quellcode:
program dp_180328;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils;

type
  TTest = class
  end;

procedure Output( const AContext : string; AInstance : TTest );
begin
  if Assigned( AInstance )
  then
    Writeln( AContext, ': ', Integer( AInstance ) )
  else
    Writeln( AContext, ': NIL' )

end;

procedure CallByValue( AInstance : TTest );
begin
  AInstance := TTest.Create;
  Output( 'CallByValue', AInstance );
end;

procedure CallByReference( var AInstance : TTest );
begin
  AInstance := TTest.Create;
  Output( 'CallByReference', AInstance );
end;

procedure Call( const AInstance : TTest );
begin
  // Auskommentiert, da es hier einen Compiler-Fehler gibt!
  // AInstance := TTest.Create;
  // Output( 'Call', AInstance );
end;

procedure Main;
var
  LTest : TTest;
begin
  LTest := TTest.Create;
  try
    Output( 'Main', LTest );
    CallByValue( LTest );
    Output( 'Main', LTest );
    CallByReference( LTest );
    Output( 'Main', LTest );
    Call( LTest );
    Output( 'Main', LTest );
  finally
    LTest.Free;
  end;
end;

begin
  try
    Main;
  except
    on E : Exception do
      Writeln( E.ClassName, ': ', E.Message );
  end;
  ReadLn;

end.
Die Ausführung ergibt dann
Code:
Main           : 31925952
CallByValue    : 31925968
Main           : 31925952
CallByReference: 31925984
Main           : 31925984
Main           : 31925984

himitsu 12. Mai 2014 17:48

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Mit dem CONST, VAR und OUT bestimmt man nur, ob der "Inhalt" der Variable verändert werden kann.
Bei einem Integer ist das der Zahlenwert, aber bei einem Objekt ist der nur der interne Instanz-Zeiger.

Ohne VAR kann man keine Andere Instanz eines Objektes zurückgeben, aber der "Inhalt" des Objektes ist davon nicht betroffen.

Prinzipiell wird der "Inhalt" aller Pointer-Objekte nicht beeinflusst.
Außer bei Strings (AnsiString, WideString und UnicodeString, ausgeschlossen den ShortStrings, welche ja wie Records reagieren), denn dort sorgt der Compiler für eine Prüfung und "verbietet" Schreibzugriffe, wenn der Parameter CONST ist und ansonsten sorgt der Compiler dort für die entsprechende Behandlung, indem der Inhalt automatisch "umkopiert" wird, bei einem Schreibzugriff.

Sir Rufo 12. Mai 2014 17:58

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Wenn man weiß was bei einer Instanz übergeben wird (nur eine Referenz, quasi eine Visitenkarte), dann versteht man auch die Delphi-Dokumentation.

Mit dem
Delphi-Quellcode:
var
wird die gleiche Visitenkarte (Referenz) übergeben. Bemale ich die, dann ist die bemalt.

Ohne dem
Delphi-Quellcode:
var
wird eine Kopie der Visitenkarte (Referenz) übergeben. Bemale ich die, dann ist eben diese Kopie bemalt und das Original bleibt unverändert.

Mit dem
Delphi-Quellcode:
const
wird die Visitenkarte (Referenz) als unbemalbares Objekt (Read-Only) übergeben und kann gar nicht bemalt werden.

OlafSt 13. Mai 2014 10:39

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Keiner hat die Frage wirklich verstanden, ergo hat sie auch keiner wirklich beantwortet. Ist übrigens eine Frage, die ich mir vor langer Zeit auch mal stellte und dann einfach beschloß "ist halt so":

Ich nehme einen
Delphi-Quellcode:
TFileStream
und öffne eine Datei.
Nun rufe ich eine Methode auf, und übergebe diesen Stream als Parameter.
In dieser Methode wird nun per
Delphi-Quellcode:
Seek
-Methode des
Delphi-Quellcode:
TFileStreams
an das Ende der Datei gesprungen.

Es ist echt schnurzpiepegal, ob ich den TFileStream als
Delphi-Quellcode:
CONST
,
Delphi-Quellcode:
VAR
oder auch normal übergebe, die Seek-Methode verschiebt den Dateizeiger ans Ende der Datei. Ergo verändere ich doch den
Delphi-Quellcode:
TFileStream
(wenigstens seinen Dateizeiger !), was doch dem
Delphi-Quellcode:
CONST
widerspricht.

Ich habe das irgendwann einfach so akzeptiert, das CONST nicht vollständig auf Objekte wirkt.

DeddyH 13. Mai 2014 10:51

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Du änderst nur Eigenschaften des Streams, aber nicht den Stream (also die übergebene Instanz) selbst. Das wäre aber möglich, wenn man ihn als Var-Parameter übergibt, und kann zu sehr hässlichen Nebenwirkungen führen, wenn man nicht weiß, was man da tut. Ein Negativbeispiel findet sich z.B. hier (unten unter "Vorsicht bei Zeigertypen!").

Sir Rufo 13. Mai 2014 11:15

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
@OlafSt

Wenn ich dir meinen Schlüssel zu meiner Wohnung gebe, dann kannst du in die Wohnung rein und dort umräumen.
Tauscht du den Schlüssel nun aus und gibts mir diesen zurück, komme ich nicht mehr in meine Wohnung.
Delphi-Quellcode:
procedure Foo( var AKey : TWohnung );
Gebe ich dir eine Kopie meines Schlüssels, dann kannst du immer noch in die Wohnung rein und umräumen.
Da ich aber keinen Schlüssel von dir zurücknehme, kann ich trotzdem noch mit meinem Schlüssel in meine Wohnung (und sehe dann entweder Chaos oder endlich mal eine aufgeräumte Wohnung)
Delphi-Quellcode:
procedure Foo( AKey : TWohnung );

OlafSt 13. Mai 2014 11:18

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
...oder eine leere wohnung :lol:

Ich persönlich habe das ganze ja schon länger verstanden (auch wenn ich der Ansicht bin, das CONST nun mal CONST ist, auch Eigenschaften sollten dann unveränderlich sein).

Ich hoffe, der TE bekam nun seine Antwort ;)

Sir Rufo 13. Mai 2014 11:20

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Zitat:

Zitat von OlafSt (Beitrag 1258724)
...oder eine leere wohnung :lol:

Ich persönlich habe das ganze ja schon länger verstanden (auch wenn ich der Ansicht bin, das CONST nun mal CONST ist, auch Eigenschaften sollten dann unveränderlich sein).

Ich hoffe, der TE bekam nun seine Antwort ;)

Ähm, wenn das so sein sollte, dann dürften auch keine Methoden ausgeführt werden, denn die könnten ja Eigenschaften verändern. Wofür will ich dann eine Instanz übergeben?
(Es wird ja gar nicht die Instanz übergeben, nur der Weg, wie ich dort hinkomme und bei
Delphi-Quellcode:
const
kann ich den Weg auch gar nicht ändern)

bernau 13. Mai 2014 14:34

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Zitat:

Zitat von Sir Rufo (Beitrag 1258638)
Wenn man weiß was bei einer Instanz übergeben wird (nur eine Referenz, quasi eine Visitenkarte), dann versteht man auch die Delphi-Dokumentation.

Mit dem
Delphi-Quellcode:
var
wird die gleiche Visitenkarte (Referenz) übergeben. Bemale ich die, dann ist die bemalt.

Ohne dem
Delphi-Quellcode:
var
wird eine Kopie der Visitenkarte (Referenz) übergeben. Bemale ich die, dann ist eben diese Kopie bemalt und das Original bleibt unverändert.

Mit dem
Delphi-Quellcode:
const
wird die Visitenkarte (Referenz) als unbemalbares Objekt (Read-Only) übergeben und kann gar nicht bemalt werden.


Besser kann man es nicht erklären. :thumb:

Da solltest du dir ein Patent drauf geben lassen.

bernau 13. Mai 2014 14:36

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Zitat:

Zitat von OlafSt (Beitrag 1258720)
Keiner hat die Frage wirklich verstanden, ergo hat sie auch keiner wirklich beantwortet.

Süß ;-)

Sir Rufo hat es genau richtig beschrieben.:idea:

michele_tedesco 14. Mai 2014 11:10

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Zitat:

Zitat von Sir Rufo (Beitrag 1258723)
@OlafSt

Wenn ich dir meinen Schlüssel zu meiner Wohnung gebe, dann kannst du in die Wohnung rein und dort umräumen.
Tauscht du den Schlüssel nun aus und gibts mir diesen zurück, komme ich nicht mehr in meine Wohnung.
Delphi-Quellcode:
procedure Foo( var AKey : TWohnung );
Gebe ich dir eine Kopie meines Schlüssels, dann kannst du immer noch in die Wohnung rein und umräumen.
Da ich aber keinen Schlüssel von dir zurücknehme, kann ich trotzdem noch mit meinem Schlüssel in meine Wohnung (und sehe dann entweder Chaos oder endlich mal eine aufgeräumte Wohnung)
Delphi-Quellcode:
procedure Foo( AKey : TWohnung );

In deinem Beispiel wird aber die Wohnung übergeben und dicht der Schlüssel :-)

Wenn ich an der Kopie des Schlüssels etwas ändere (Eigenschaft: Farbe, oder Form), dann ist bei
Delphi-Quellcode:
procedure Foo( AKey : TKey );
dann ist doch auch der Original-Schlüssel geändert, richtig?

Gibt es eine Möglichkeit (gibt es überhaupt Situationen) wo das Objekt vollkommen als Kopie übergeben wird??

Ist das Verhalten in .Net das selbe?

Neutral General 14. Mai 2014 11:13

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Zitat:

Zitat von michele_tedesco (Beitrag 1258855)
Gibt es eine Möglichkeit (gibt es überhaupt Situationen) wo das Objekt vollkommen als Kopie übergeben wird??

Ist das Verhalten in .Net das selbe?

Es gibt keine Möglichkeit dass das Objekt als Kopie übergeben wird. Wenn du das brauchst, dann musst du überlegen ob du auf Records umsteigen willst/kannst.

In .NET ist das Verhalten soweit ich weiß das selbe.

michele_tedesco 14. Mai 2014 11:14

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Danke an alle schon Mal :-)
Ich habe verstanden wie sich ein Objekt als Parameter verhaltet :-)
Ziel erreicht!

Sir Rufo 14. Mai 2014 11:38

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Zitat:

Zitat von michele_tedesco (Beitrag 1258855)
Zitat:

Zitat von Sir Rufo (Beitrag 1258723)
@OlafSt

Wenn ich dir meinen Schlüssel zu meiner Wohnung gebe, dann kannst du in die Wohnung rein und dort umräumen.
Tauscht du den Schlüssel nun aus und gibts mir diesen zurück, komme ich nicht mehr in meine Wohnung.
Delphi-Quellcode:
procedure Foo( var AKey : TWohnung );
Gebe ich dir eine Kopie meines Schlüssels, dann kannst du immer noch in die Wohnung rein und umräumen.
Da ich aber keinen Schlüssel von dir zurücknehme, kann ich trotzdem noch mit meinem Schlüssel in meine Wohnung (und sehe dann entweder Chaos oder endlich mal eine aufgeräumte Wohnung)
Delphi-Quellcode:
procedure Foo( AKey : TWohnung );

In deinem Beispiel wird aber die Wohnung übergeben und dicht der Schlüssel :-)

Ein absolutes, definitives und ultimatives NEIN!

Es wird eben nicht die Instanz übergeben, sondern eine Referenz/Zeiger/Pointer (wie auch immer du das definieren willst) auf die Instanz.

Zitat:

Zitat von michele_tedesco (Beitrag 1258855)
Wenn ich an der Kopie des Schlüssels etwas ändere (Eigenschaft: Farbe, oder Form), dann ist bei
Delphi-Quellcode:
procedure Foo( AKey : TKey );
dann ist doch auch der Original-Schlüssel geändert, richtig?

Es wird nicht der Schlüssel (Referenz/Zeiger/Pointer) geändert, sondern die Instanz!
Und selbst wenn du in dem Falle den Wert von
Delphi-Quellcode:
AKey
änderst, dann wird der Wert von
Delphi-Quellcode:
procedure Foo( AKey : TWohnung );
begin
  AKey := TWohnung.Create; // In AKey steht jetzt der Wert $22222222
end;

procedure Bar;
var
  LKey : TWohnung;
begin
  LKey := TWohnung.Create; // <-- In LKey steht jetzt z.B. der Wert $11111111
  Foo( LKey );
  // LKey hat jetzt immer noch den Wert $11111111
end;
Delphi-Quellcode:
LKey
nicht geändert.

DeddyH 14. Mai 2014 12:33

AW: Unterschied zwischen (var AFileStream: TFileStream) und (AFileStream: TFileStream
 
Holen wir mal ein bisschen aus:
Delphi-Quellcode:
procedure TfrmSchiessMichTot.MachWas;
var
  //Instanzvariable
  Dingens: TDingens;
begin
  //Erzeugen der Instanz und Ablegen in der Variablen (intern ein Zeiger)
  Dingens := TDingens.Create;
Dingens ist also ein Zeiger auf die Instanz, die mit dem Create-Aufruf angelegt wurde.
Delphi-Quellcode:
procedure MachDingens(ADingens: TDingens);
//ADingens ist hier eine Kopie der ursprünglichen Instanzvariablen,
//zeigt aber somit auch auf dieselbe Instanz, daher lässt sich darüber auf
//deren Methoden und Eigenschaften zugreifen
Folgender Code schadet also nicht, da er nur die Kopie betrifft:
Delphi-Quellcode:
procedure MachDingens(ADingens: TDingens);
begin
  ADingens := TDingens.Create;
Delphi-Quellcode:
procedure MachDingens(var ADingens: TDingens);
//Hier handelt es sich um die Original-Instanzvariable. Überschreibt man die,
//hat man auch in der aufrufenden Routine keinen Zugriff mehr auf die
//ursprüngliche Instanz
Weist man hier nun dem Parameter einen neuen Wert zu, betrifft das auch die Originalvariable, die somit in der aufrufenden Routine nicht mehr erreichbar ist. Bestenfalls bewirkt das lediglich ein Speicherleck, kann aber auch sehr viel schlimmer ausgehen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:14 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz