![]() |
Delphi-Version: 5
Properties haben nach Zuweisung falsche Werte
Ich habe ein Form mit einem Button. Dieser holt sich über einen Funktion einen Vornamen und Namen. Zugeweisen wird:
Adresse.Vorname := 'a'; Adresse.Name := 'b'; Wenn Vorname und Name zusammen ausgegeben wird aber "b b" angezeigt. Richtig wäre "a b". Wie kann das sein? Mit anderen Texten in der Zuweisung klappt es manchmal und manchmal nicht. Das ist sehr merkwürdig.
Delphi-Quellcode:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TAdresse = class(TPersistent) private FVorname: string; FName: string; public procedure Assign(Source: TPersistent); override; property Vorname: string read FVorname write FVorname; property Name: string read FName write FName; end; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private function GetDebitorName: string; function GetReAdr: TAdresse; protected Adresse: TAdresse; public end; var Form1: TForm1; implementation {$R *.dfm} function TForm1.GetReAdr: TAdresse; begin Adresse.Vorname := 'a'; Adresse.Name := 'b'; Result := Adresse; end; procedure TForm1.FormCreate(Sender: TObject); begin Adresse := TAdresse.Create; end; procedure TForm1.FormDestroy(Sender: TObject); begin Adresse.Free; end; function TForm1.GetDebitorName: string; begin Result := GetReAdr.Vorname + ' ' + GetReAdr.Name; end; procedure TForm1.Button1Click(Sender: TObject); begin caption := GetDebitorName; end; procedure TAdresse.Assign(Source: TPersistent); begin if Source is TAdresse then begin Vorname := (Source as TAdresse).Vorname; Name := (Source as TAdresse).Name; end else inherited; end; end. |
AW: Properties haben nach Zuweisung falsche Werte
Es ist nie eine gute Idee in Methoden auf globale Variablen zuzugreifen.
Delphi-Quellcode:
function TForm1.GetReAdr: TAdresse;
begin Result.Vorname := 'a'; Result.Name := 'b'; end; |
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
property Vorname: string read FVorname write FVorname; ist FVorname auch global in der Klasse gültig. Ähnlich wie Adresse hier. |
AW: Properties haben nach Zuweisung falsche Werte
Wenn Du es besser als ich weisst, warum fragst Du dann?
|
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
|
AW: Properties haben nach Zuweisung falsche Werte
Hier nochmal eine Ergänzung, wie GetReAdr noch SQL ausführt:
Delphi-Quellcode:
function TForm1.GetReAdr: TAdresse;
begin if Adresse.IsEmpty then begin Query.HoleAufwändigDatenAb; Adresse.Vorname := Query.FieldByName('Vorname').asString; Adresse.Name := Query.FieldByName('Name').asString; end; Result := Adresse; end; |
AW: Properties haben nach Zuweisung falsche Werte
Hallo,
vielleicht liefert ja dein Query.HoleAufwändigDatenAb; was falsches ab? Breakpoint + Watchpoint drauf. du kannst auch eine Set-Methode für VorName schreiben und dort einen bedingten Breakpoint setzen, wenn 'b' übergeben wird. Ausserdem auch hier einen Breakpoint setzen procedure TAdresse.Assign(Source: TPersistent); Vielleicht überschreibst du auch irgendwo Speicher -> FastMM4 benutzen. Heiko |
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
|
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
|
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
Aber er hat nicht auf seinen Compiler gehört, welcher ihm bestimmt was vonwegen "Result ist nicht initialisiert" um die Ohren wirft! :roll: [edit] :gruebel: War das
Delphi-Quellcode:
vorhin auch schon in Post #1 ?
Result := Adresse;
Nja, dein Code war zumindestens falsch, denn Result wurde nicht initialisiert.
Delphi-Quellcode:
function TForm1.GetReAdr: TAdresse;
begin Result := Adresse; Result.Vorname := 'a'; Result.Name := 'b'; end; // oder function TForm1.GetReAdr: TAdresse; begin Adresse.Vorname := 'a'; Adresse.Name := 'b'; Result := Adresse; end; |
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
|
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
|
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
|
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
|
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
|
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
|
AW: Properties haben nach Zuweisung falsche Werte
Ich kann das hier (XE7) reproduzieren. Es handelt sich offenbar um einen Compiler-Fehler.
Delphi-Quellcode:
Das erste result bringt noch das richtige Ergebnis, aber beim zweiten klappts nicht.
var
sn: string; sv: string; begin sv := GetReAdr.Vorname; sn := GetReAdr.Name; result := sv + ' ' + sn; Result := GetReAdr.Vorname + ' ' + GetReAdr.Name; end; |
AW: Properties haben nach Zuweisung falsche Werte
Ich kann es mit D6 auch bestätigen.
|
AW: Properties haben nach Zuweisung falsche Werte
Ist .Name vllt. ein reserviertes Wort in dem Zusammenhang? Zumindest wird es ja hier im Codeformatierer der DP fett blau gezeigt?
|
AW: Properties haben nach Zuweisung falsche Werte
Es funktioniert auch, wenn man die Adresse in einer lokalen Variablen zwischenspeichert.
Delphi-Quellcode:
function TForm1.GetDebitorName: string;
var a: TAdresse; begin a := GetReAdr; Result := a.Vorname + ' ' + a.Name; end; |
AW: Properties haben nach Zuweisung falsche Werte
Mit ShortStrings funktionierts, d.h. irgendwie verpointert der sich.
|
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
Delphi-Quellcode:
Edit: Andersrum gemischt geht's dann doch wieder:
function TForm1.GetDebitorName: string;
var a:Tadresse; begin a:=GetReAdr; Result := a.Vorname + ' ' + GetReAdr.Name; end;
Delphi-Quellcode:
Result := GetReAdr.Vorname + ' ' + a.Name;
|
AW: Properties haben nach Zuweisung falsche Werte
Es hat offenbar etwas mit der internen Realisierung des String-Results als var-Parameter und der nicht-vorhandenen Referenzzählung bei String-Konstanten zu tun. Aber da mögen sich die Compiler-Leute von Embarcadero dran auslassen.
Wer schreibt den QC? |
AW: Properties haben nach Zuweisung falsche Werte
Das ist ein "Fehler" im CodeHiglighting, dann das kann dieses nicht richtig auflösen, nur anhand eines Codeabschnittes.
"Name" ist nur reserviert, innerhalb einer Property-Deklaration und bei Prozedurimporten (wo es wiederum der SyntaxHighlighter in der IDE nicht hinbekommt) |
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
|
AW: Properties haben nach Zuweisung falsche Werte
Da der Fehler erst durch Deinen Thread bekannt geworden ist, lehne ich mich mal weit aus dem Fenster und behaupte, dass das so immer funktionieren sollte. Haftbar lasse ich mich für diese Aussage allerdings nicht machen :angel2:
|
AW: Properties haben nach Zuweisung falsche Werte
Selbst so geht es nicht:
Delphi-Quellcode:
Irgendwie kann ich mir nicht vorstellen, dass noch nie jemand seit Delphi 6 so eine Property mit einem Getter genutzt hat und dass das erst jetzt auffällt.
property AdrProp: TAdresse read GetAdrProp write SetAdrProp;
function TForm1.GetAdrProp: TAdresse; begin Adresse.Vorname := 'a'; Adresse.Name := 'b'; result := Adresse; end; |
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
Delphi-Quellcode:
und der Zusammensetzung des Ergebnisses. Hier versucht der Compiler offenbar eine coole Optimierung und fällt dabei auf die Klappe. Es scheint ja gut zu funktionieren, wenn man die Strings nicht mit einem zweimaligen Aufruf der Funktion zusammensetzt. Die Felder der TAdresse-Instanz sind ja durchaus korrekt. Es ist das Concat was schief geht.
GetReAdr
Auf den zweimaligen Aufruf hatte ich ja schon anfangs hingewiesen. Offenbar ist er nicht nur redundant sondern auch schädlich. |
AW: Properties haben nach Zuweisung falsche Werte
Egal ob Optimierung an oder aus, stimmt.
Und noch interessanter: Der Fehler besteht nur beim Win32-Compiler, bei Win64 nicht. Andere Compiler kann ich nicht testen. Könnte man folgendes als Minimalbeispiel in die QC eintragen?
Delphi-Quellcode:
Oder ginge das noch kürzer?
program Project4;
{$APPTYPE CONSOLE} type TMyClass = class one, two: String; end; var globalInstance: TMyClass; function getInstance(): TMyClass; begin if not Assigned(globalInstance) then globalInstance := TMyClass.Create(); globalInstance.one := 'one'; globalInstance.two := 'two'; Result := globalInstance; end; function getBoth(): String; var oneCopy, twoCopy: String; begin oneCopy := getInstance().one; twoCopy := getInstance().two; WriteLn( oneCopy + ' ' + twoCopy ); Result := getInstance().one + ' ' + getInstance().two; end; begin WriteLn( getBoth() ); // Output on DCC32: //one two //two two // Output on DCC64: //one two //one two end. |
AW: Properties haben nach Zuweisung falsche Werte
Wäre es denn im konkreten Fall nicht besser, das ganze Konstrukt irgendwie zu ändern?
Eine Prozedur FillAdresse, die die Felder füllt und dann die Funktion, die die gewünschten Felder "concatiert" (gibt's das?) ausgibt? |
AW: Properties haben nach Zuweisung falsche Werte
@Der schöne Günther: Dein Code ist eine gute Zusammenfassung und zeigt mit Delphi 2010 den Fehler. Stellt das jemand bitte in den QC ein? Ich kann mich seit 20 Minuten nicht einloggen... Danke!
|
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
|
AW: Properties haben nach Zuweisung falsche Werte
Das Ungewöhnliche ist wohl das hier:
Delphi-Quellcode:
Die Zuweisung von 'a' und 'b' im 'Getter' der ReAdr.
function TForm1.GetReAdr: TAdresse;
begin Adresse.Vorname := 'a'; Adresse.Name := 'b'; Result := Adresse; end; Man würde die Eigenschaften der Adresse einmalig setzen z.B. per LazyLoad. Oder man würde gleich eine protected Property aus der Adresse machen. |
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
Auch wenn es in dem Fall OK ist, wenn "Name" als Property verwendet wird. Ich würde diese Bezeichnung nicht verwenden. Habe früher schon oft nach Fehlern gesucht, weil "Name" dann doch häufiger in Klassen verwendet wird. Ich verwende für So etwas immer "Vorname" und "Nachname". Das ist dann eindeutiger. Aber natürlich Geschmackssache. |
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
|
AW: Properties haben nach Zuweisung falsche Werte
Lazy Load sorgt dafür, das solche Zuweisungen nur einmal durchgeführt werden.
Delphi-Quellcode:
Allgemein ist das LazyLoad-Pattern für Properties so:
// Also so gehts:
Function TForm1.GetReAdr: TAdresse; Begin If fAddress = Nil Then Begin Adresse.Vorname := 'a'; Adresse.Name := 'b'; fAddress := Adresse; End; Result := fAddress; End; // Und so auch Function TForm1.GetReAdr: TAdresse; Begin If not fInitialized Then Begin Adresse.Vorname := 'a'; Adresse.Name := 'b'; fInitialized := True; End; Result := Adresse; End;
Delphi-Quellcode:
Damit wird nur beim Zugriff, und dann nur beim ersten, instantiiert. Früher nannte man das 'Fetch on demand'. Heute eben 'lazy load'. Und damit schlägt man dem blöden Compiler ein Schnippchen.
Function TMyClass.GetProperty : TSomeValue;
Begin if fProperty=nil then fProperty := CreateSomeValue; result := fProperty; End; |
AW: Properties haben nach Zuweisung falsche Werte
Wie gesagt LazyLoad wird verwendet (siehe Post #6) und ändert am Problem nichts...
|
AW: Properties haben nach Zuweisung falsche Werte
Das Problem ist die Referenz-Zählung für die Strings. Bei jeder Zuweisung wird der String an eine andere Speicheradresse geschrieben und die alte Speicheradresse wird als frei markiert.
Es taucht nicht auf, wenn man für die Eigenschaften
Delphi-Quellcode:
und
Name
Delphi-Quellcode:
einen Getter benutzt
Vorname
Delphi-Quellcode:
Weiterhin taucht es nicht auf, wenn man die Adresse so zusammensetzt
type
TAdresse = class private FVorname: string; FName: string; function GetName: string; function GetVorname: string; public property Vorname: string read GetVorname write FVorname; property Name: string read GetName write FName; end;
Delphi-Quellcode:
Und wenn man sich mal spasseshalber die Pointer auf die Variablen holt
function TFoo.GetFullName : string;
begin Result := GetAdresse.Vorname + GetAdresse.Nachname; end;
Delphi-Quellcode:
Dieses ist aber eher Zufall, da die beiden Speicherstellen abwechselnd belegt werden, was wir mit folgendem Code überprüfen können
function TTest.GetFullName : string;
var LVorname, LName : PChar; begin LVorname := PChar( GetAdresse.Vorname ); // jetzt zeigt der Debugger den korrekten Vornamen bei LVorname an LName := PChar( GetAdresse.Name ); // ab jetzt steht in LVorname und LName der gleiche Wert, weil nun die Pointer an die gleiche Stelle zeigen Result := GetAdresse.Vorname + GetAdresse.Nachname; end;
Delphi-Quellcode:
Hier die Inhalte jeweils nach der Codezeile
function TTest.GetFullName: string;
var LName1, LVorname, LName2 : PChar; begin {1} LName1 := PChar( GetAdresse.Name ); {2} LVorname := PChar( GetAdresse.Vorname ); {3} LName2 := PChar( GetAdresse.Name ); end;
Delphi-Quellcode:
natürlich einwandfrei funktioniert. Warum?
function TTest.GetFullNameReverse: string;
begin Result := GetAdresse.Name + ' ' + GetAdresse.Vorname; end;
|
AW: Properties haben nach Zuweisung falsche Werte
Zitat:
@Sir Rufo: Nee, oder? Verrückt! Tolle Analyse! Gut, das ich bei C# bin. :mrgreen: |
AW: Properties haben nach Zuweisung falsche Werte
Unter Win64 ist der Pointer-Reigen auch zu beobachten, allerdings sind die Ergebnisse immer korrekt.
Somit wird unter Win64 wohl der Speicherbereich sofort kopiert und bei Win32 wird sich der Pointer gemerkt und zum Schluss zusammenkopiert (mit dem bekannten Ergebnis). Nur so eine Vermutung und ich will da jetzt auch nicht zu sehr in die Tiefe gehen :mrgreen: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:50 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz