Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Record als Konstantenobjekt nach Update D2009->Delphi XE (https://www.delphipraxis.net/162591-record-als-konstantenobjekt-nach-update-d2009-delphi-xe.html)

BoolString 30. Aug 2011 15:58

Record als Konstantenobjekt nach Update D2009->Delphi XE
 
Schönenn guten Abend,

ich habe gerade ein Update von Delphi 2009 auf Delphi XE durchgeführt. Beim Start eines Projektes, durfte ich feststellen, daß eine alte Unit nicht mehr funktioniert.

Mir ist derzeit nicht ganz klar, wo der Fehler liegt. Vielleicht kann mir hier jemand einen Tipp geben. Ich bekomme die Fehlermeldung, daß ich hier ein jetzt Konstantenobjekt habe und als Folgefehler daraus die Meldung, daß nichts zugewiesen werden kann. Möglicherweise liegt es an einigen Veränderungen zwischen D2009->DXE.

Ich hoffe ich habe hier alle relevanten Teile für eine Beispielprozedur rauskopiert.

Delphi-Quellcode:
unit uFLPReader;

interface

uses
  Windows, Classes, SysUtils;



type
 TAlgenklassenparameter = packed record
    _Name: AnsiString;
    _FlagUseInFit: Longint;
    _Fingerprint_Standardabweichung: array[1..16] of Double;
    _LEDOffset: Double;
  end;

  TParametersOfFit1 = packed record
    _Algenklassenparameter: array[1..8] of TAlgenklassenparameter;
  end;



 TFLPModule = class(TObject)
  private
    FParametersOfFit1: TParametersOfFit1;
    procedure ReadParametersOfFit1(const AStream: TStream);
public
    property ParametersOfFit1: TParametersOfFit1 read FParametersOfFit1;
end;

implementation

procedure TFLPModule.ReadParametersOfFit1( const AStream: TStream);
var
  i                : Integer;
  NameLen          : Longint;
  Dummy            : AnsiString;
begin
  for i := 1 to 8 do
    with ParametersOfFit1._Algenklassenparameter[i] do
    begin
      AStream.ReadBuffer(NameLen, SizeOf(NameLen));
      SetLength (_Name, NameLen);                                                                    // hier ein Fehler
      if NameLen > 0 then
      Begin
        AStream.ReadBuffer(_Name[1], NameLen);                                                       // hier ein Fehler
        _Name := Copy (_Name, 0, NameLen-1);
      end
      Else
      Begin
        _Name := '';
      end;
      AStream.ReadBuffer(_FlagUseInFit, SizeOf(_FlagUseInFit));                                      // hier ein Fehler
      AStream.ReadBuffer(_Fingerprint_Standardabweichung, SizeOf(_Fingerprint_Standardabweichung));  // hier ein Fehler
      AStream.ReadBuffer(_LEDOffset, SizeOf(_LEDOffset));                                            // hier ein Fehler
    end;
end;
Analog gibt es noch andere Stellen, wo der Fehler meiner Meinung nach aber die selbe Ursache haben dürfte. Bisher habe ich mit dieser Unit nie Probleme gehabt. Ich schleppe sie seit D7 in einem Projekt mit durch. Möglicherweise bin ich gerade mit Blindheit geschlagen, aber ich komm nicht drauf.


Herzlichen Dank,

Jan

mkinzler 30. Aug 2011 16:02

AW: Record als Konstantenobjekt nach Update D2009->Delphi XE
 
Mal mit ohne with versucht?

Blup 30. Aug 2011 16:11

AW: Record als Konstantenobjekt nach Update D2009->Delphi XE
 
ParametersOfFit1 ist als property nur "read" deklariert, FParametersOfFit1 verwenden.

himitsu 30. Aug 2011 16:12

AW: Record als Konstantenobjekt nach Update D2009->Delphi XE
 
In 2010/XE gab es ein paar Änderungen bezüglich der Zugriffsprüfungen von Const-Parametern und anderen Konstanten.

Kann schon sein, daß sich da ein Bug versteckt hat.


PS:
Delphi-Quellcode:
Copy (_Name, 0, NameLen-1);
... 0?


Zitat:

ParametersOfFit1 ist als property nur "read" deklariert, FParametersOfFit1 verwenden.
Stimmt, also ist diese Meldung wohl richtig
und der Code hätte nie funktionieren dürfen, da man die Inhalte von Record-Parametern nicht ändern kann, es sei denn man weist einen kompletten Record zu.
Änderungen an einzelnen Feldern werden nur an der Kopie des Property vorgenommen und nicht an dem, worauf das Property zeigt ... darum diese Meldung.

[edit] durch die Codeoptimierung funktionierte es zufällig doch mal



Du mußt dort auf FParametersOfFit1 zugreifen. :warn:

USchuster 30. Aug 2011 16:49

AW: Record als Konstantenobjekt nach Update D2009->Delphi XE
 
Zitat:

Zitat von himitsu (Beitrag 1120410)
In 2010/XE gab es ein paar Änderungen bezüglich der Zugriffsprüfungen von Const-Parametern und anderen Konstanten.

Die Änderungen gab es in D2010.

Zitat:

Zitat von himitsu (Beitrag 1120410)
Zitat:

ParametersOfFit1 ist als property nur "read" deklariert, FParametersOfFit1 verwenden.
Stimmt, also ist diese Meldung wohl richtig
und der Code hätte nie funktionieren dürfen,

Der dazugehörige Bug wurde in D2010 gefixed.

Report No: 56893 (RAID: 257191) Status: Closed
with <> ".": Syntax ambiguity?!
http://qc.embarcadero.com/wc/qcmain.aspx?d=56893

Zitat:

Zitat von himitsu (Beitrag 1120410)
Änderungen an einzelnen Feldern werden nur an der Kopie des Property vorgenommen und nicht an dem, worauf das Property zeigt ... darum diese Meldung.

Und das konnte zu scheinbarem Datenverlust kommen, da nicht das passierte was der Programmierer wollte. Folgender Ausschnitt stammt aus einem Duplikat von QC 56893.

Delphi-Quellcode:
  with t.Items[0] do
  begin
    writeln(s); // str
    s := ':(';
    b := true;
    writeln(b); // true
  end;
  writeln(t.Items[0].s); // str <-- data loss
  writeln(t.Items[0].b); // false <-- data loss

himitsu 30. Aug 2011 17:11

AW: Record als Konstantenobjekt nach Update D2009->Delphi XE
 
Sobald lesend auf ein Property zugegriffen wird (also nicht Property := ...), wird READ verwendet, welches eine lokale Kopie des Wertes erstellt.
Alles was in dieser Kopie verändert wird, hat keinen Einfluß auf das Property.
[add] (bei direkten Zugriffen auf Felder ht es doch Einfluß, was aber so nicht erlaubt ist)

Darum wirft man hier nun auch endlich mal einen Compiler-Fehler.


Was aber leider auf Record-Methoden noch nicht zutrifft, da man diese nicht als "Konstant" deklarieren kann.
Ist schon witzig, wie man darüben dennoch eine Konstante verändern kann. :roll:

Dieser Code
Delphi-Quellcode:
with t.Item do
begin
  s := ':(';
  writeln(s); // str
  b := true;
  writeln(b); // true
end;
writeln(t.Item.s); // str <-- data loss
writeln(t.Item.b); // false <-- data loss
t.Item.s := ':)';
writeln(t.Item.s); // str <-- data loss
macht nun Folgendes:
Delphi-Quellcode:
Temp1 := t.Item; // t.Item {Getter}
begin
  Temp1.s := ':(';
  writeln(Temp1.s);
  Temp1.b := true;
  writeln(Temp1.b);
end;
Temp2 := t.Item;
writeln(Temp2.s);
Temp3 := t.Item;
writeln(Temp3.b);
Temp4 := t.Item;
Temp4.s := ':)';
Temp5 := t.Item;
writeln(Temp5.s);
(die Codeoptimierung ist natürlich so schlau möglichst nur eine Temp-Variable zu nutzen und diese überall wiederzuverwenden, was aber an den auszuführenden Befehlen nichts ändert)



Da Emba ja leider nicht von selber auf die Idee kommt, hier nachher die TempVar an den Setter zurück zu übergeben, muß man es manuell machen.
Delphi-Quellcode:
MyVar := t.Item; {Getter}
with MyVar do
begin
  s := ':(';
  writeln(s);
  b := true;
  writeln(b);
end;
t.Item := MyVar; {Setter}

writeln(t.Item.s); {Getter}
writeln(t.Item.b); {Getter}

MyVar := t.Item; {Getter}
MyVar.s := ':)';
t.Item := MyVar; {Setter}

writeln(t.Item.s); {Getter}



PS: Das trifft auch auf Objekte zu, genauer auf den Objektzeiger.
Den Objektinhalt kann man natürlich ändern, da er ja nicht in dieser Kopie enthalten ist. :angle2:

PSS: Das selbe Problem hat übrigens auch die generische TList. :wall:
(vielleicht lade ich dazu ja irgendwannl meinen "Bugfix" hoch)

SirThornberry 30. Aug 2011 17:37

AW: Record als Konstantenobjekt nach Update D2009->Delphi XE
 
Zitat:

Sobald lesend auf ein Property zugegriffen wird (also nicht Property := ...), wird READ verwendet, welches eine lokale Kopie des Wertes erstellt.
Das stimmt so nicht. Vielmehr dient read/write nur der Zugriffskontrolle zur Compilezeit. Beim ShowMessage wird hier intern (asm-code) keine Kopie von fMyRecord erstellt.
Delphi-Quellcode:
TMyClass=class(TObject)
private
  fMyRecord: TMyRecord;
public
  property MyRecord: TMyRecord read fMyRecord;
end;
[...]
ShowMessage(MyClassInstance.MyRecord.Value);
Der Code beim ShowMessage ist identisch mit:
Delphi-Quellcode:
TMyClass=class(TObject)
public
  MyRecord: TMyRecord;
end;
[...]
ShowMessage(MyClassInstance.MyRecord.Value);
Wäre ja schlimm der Zugriff auf eine Variable Länger dauert nur weil man den Scope oder dergleichen einschränkt.

himitsu 30. Aug 2011 17:46

AW: Record als Konstantenobjekt nach Update D2009->Delphi XE
 
hmmmmm.

OK, wenn, dann aber nur, wenn direkt auf auf ein Feld zugegriffen wird.
Gut, so könnte es doch mal zufällig funktioniert haben. ( Codeoptimierung? )

Da aber theoretisch auch ein Getter möglich wäre, würde/dürfte es dennoch nicht gehn.
PS: Das ist/war natürlich auch ein "Sicherheitsloch", da es ja als "schreibgeschützt" deklariert war. :angle:

SirThornberry 30. Aug 2011 17:56

AW: Record als Konstantenobjekt nach Update D2009->Delphi XE
 
Natürlich funktioniert das nur wenn auf ein Feld zugegriffen wird. Aber genau darum ging es hier.
Wenn man eine Funktion hat die etwas zurück liefert sollte klar sein das mit Änderung des Funktionsergebnisses nicht das geändert wird woraus das Funktionsergebnis zusammengesetzt wird. Dafür würde ja auch die Verknüpfung fehlen:
Delphi-Quellcode:
TMyObject = class(TObject)
private
  fVal1: Integer;
  fVal2: Integer;
  function GetValues(): TMyRecord;
public
  property MyRecord: TMyRecord read GetValues;
end;
[...]
function TMyObject.GetValues(): TMyRecord;
begin
  result.Val1:=fVal1;
  result.Val2:=fVal2;
end;
Hier sollte klar sein das folgendes nicht geht da die Verknüpfung von TMyREcord.Val mit fVal1 fehlt.
Delphi-Quellcode:
MyObjectInstance.MyRecord.Val1:=Irgendwas;
Zitat:

Gut, so könnte es doch mal zufällig funktioniert haben.
Nichts mit Zufall. Logik. Es macht überhaupt keinen Sinn eine Kopie zu erstellen da zur Compilezeit der erlaubte Zugriff fest steht.
Read und Write sind Schlüsselwörter um den Zugriff zu begrenzen ebenso wie private,protected etc.
[Edit]
Diese Schlüsselwörter dienen nicht dazu den Speicher zu verschlüsseln oder andere Stolpersteine zu erzeugen um zu verhindern das man den Speicher nutzen kann. Sie dienen einzig und allein dem Begrenzen des "normalen" Zugriffs über Properties etc.

himitsu 30. Aug 2011 18:06

AW: Record als Konstantenobjekt nach Update D2009->Delphi XE
 
"zufällig" im sinne von, daß hier zufällig kein Getter verwendet wurde.

Wäre ja großer Zufall, wenn hier damals wirklich so geplant wurde, daß diese Codeoptimierung ausgenutzt werden konnte. :angle2:


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:47 Uhr.
Seite 1 von 2  1 2      

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