![]() |
Warum geht das eigentlich nicht?
Hi!
Delphi-Quellcode:
Mavarik
type
TFoo = class private FIMG : TImage; Property TheBitmap : TBitmap read FImg.Bitmap; // ??? end; |
AW: Warum geht das eigentlich nicht?
Weil der backing store ein privates Feld sein muss und nicht die Property eines privaten Feldes. Vermutlich, vielleicht, keine Ahnung wird bei einem backing store direkt die Adresse im Getter/Setter einkompiliert. Stevie und andere Spezialisten können darüber bestimmt mehr sagen.
|
AW: Warum geht das eigentlich nicht?
Anderseits ist es ja auch kein großer Aufwand, einen einzeiligen Getter zu schreiben.
|
AW: Warum geht das eigentlich nicht?
Die Frage ist eher: Was macht eine visuelle TImage versteckt in einer Komponente?
Oder wird die auch irgendwo angezeigt? > TPicture oder TGraphit (wenn man den Typ kennt) |
AW: Warum geht das eigentlich nicht?
Zitat:
|
AW: Warum geht das eigentlich nicht?
Da kann ich auch nur raten.
|
AW: Warum geht das eigentlich nicht?
Ich vermute das dort impliziert eine Methode einkompiliert wird.
![]() Ist aber eben nur eine Vermutung meinereinerseits ... Laut ![]() Könnte man also einfach ausprobieren, ob
Delphi-Quellcode:
den gleichen Code erzeugt wie
TFoo = class
private FBar : integer; public property Bar : integer read FBar write FBar; end;
Delphi-Quellcode:
TFoo = class
private FBar : integer; procedure SetBar( const Value : integer ); function GetBar : integer; public property Bar : integer read FBar write FBar; end; procedure TFoo.SetBar( const Value : integer ); begin FBar := Value; end; function TFoo.GetBar : integer; begin Result := FBar; end; |
AW: Warum geht das eigentlich nicht?
Nein, Delphi erzeugt da keine implizite Methoden.
In der RTTI ist in diesem Integer codiert, ob es sich um einen Zeiger (Offset) auf ein Feld handelt oder um eine Referenz auf eine Methode (Getter/Setter). Es ist vielmehr so, daß der Delphi-Compiler ohne Getter/Setter intern oftmals den Code so erzeigt, als wenn man direkt auf das Feld zugreift. (zumindest im Windows ... k.A. wie das beim neuen Compiler aussieht) |
AW: Warum geht das eigentlich nicht?
Zitat:
Offenbar möchte der Compiler das direkt auslösen können. Das ist bei einer Klasseninstanz, die ja erst zur Laufzeit erzeugt wird, aber nicht möglich. |
AW: Warum geht das eigentlich nicht?
Die RTTI nutzt, wie erwähnt, einen Offset.
Im compilierten Code wäre es praktisch kein Problem, aber es wird auch alles nochmal in der RTTI hinterlegt. Bei einem Record ist immer alles direkt in dem Record und kann somit auch direkt angesprochen werden. Bei einer Klasse werden praktisch zwei Adressen verschachtelt, was eben nicht "direkt" geht, vorallem da es auch noch eine Mischung aus direktem Feldzugriff und Getter/Setter sein kann. |
AW: Warum geht das eigentlich nicht?
Zitat:
Delphi-Quellcode:
ist zwar syntaktisch korrekt aber behämmert. Die Variante
..
public fFoo : Integer; Property Foo : Integer read fFoo; end;
Delphi-Quellcode:
ist Kretinismus 2.Klasse und ein Design Flaw übelster Sorte. Insofern 'muss' es sich um ein privates Feld handeln. Allerdings ist die Erwähnung von 'private' meinerseits ein Automatismus und rein syntaktisch ist das nicht notwendig.
..
protected fFoo : Integer; public Property Foo : Integer read fFoo; end; Bei deinem interessanten Beispiel bei einem Record meinst Du das hier?
Delphi-Quellcode:
Das geht mit einer Klasse nicht, weil der Getter direkt auf eine Adresse gemappt werden muss. Das sieht man an der Fehlermeldung des Compilers bei
Type
TRec1 = Record Foo : Integer; End; TRec = Record Rec1 : TRec1; Property Foo : Integer Read Rec1.Foo; End;
Delphi-Quellcode:
Type
TRec1 = class Foo : Integer; End; TRec = class Rec1 : TRec1; Property Foo : Integer Read Rec1.Foo; End; Zitat:
|
AW: Warum geht das eigentlich nicht?
Das wäre übrigens eine der Spracherweiterungen, die ich mir für Delphi wünschen würde.
Natürlich kann man sich heute schon einen Getter schreiben, aber die Frage ist doch: warum kann der Compiler das denn bitteschön nicht für mich machen? |
AW: Warum geht das eigentlich nicht?
Zitat:
Delphi-Quellcode:
(oder auch eines
record
Delphi-Quellcode:
) zu einer Klasseninstanz ist, dass bei ersterem das komplette Record Teil des für die Klasse alloziierten Feldbereichs ist. Bei letzterem ist nur der Zeiger auf die Klasseninstanz Teil des Feldbereichs. Das sieht man auch sehr schön, wenn man
object
Delphi-Quellcode:
verwendet:
TObject.InstanceSize
Delphi-Quellcode:
Ausgabe:
program tinstancesize;
{$apptype console} {$ifdef fpc} {$mode objfpc} {$endif} type TTestRec = record Foo: LongInt; Bar: Int64; Blubb: Boolean; end; TTestClass1 = class Rec: TTestRec; end; TTestClass2 = class Obj: TObject; end; begin Writeln(TTestClass1.ClassName, ': ', TTestClass1.InstanceSize); Writeln(TTestClass2.ClassName, ': ', TTestClass2.InstanceSize); end.
Code:
Der Compiler kann nun also das Felder im Record direkt über einen Offset addressieren, während er bei einer Klasseninstanz eine Methode generieren müsste, um den Zugriff über die RTTI nicht allzu kompliziert zu gestalten, da man das ja beliebig tief schachteln kann:
TTestClass1: 32
TTestClass2: 8
Delphi-Quellcode:
program trecprop;
{$apptype console} {$ifdef fpc} {$mode objfpc} {$endif} type TTestRec1 = record Foo: LongInt; end; TTestRec2 = record Bar: TTestRec1; end; TTestClass = class Rec: TTestRec2; property Foo: LongInt read Rec.Bar.Foo; end; begin end. Zitat:
Delphi-Quellcode:
ist? Ich mein, klar, der Compiler könnte entweder die Access Violation einfach durchlassen oder aber einen speziellen Exceptiontyp auslösen, aber wann rechnet man als Verwender der Klasse mit sowas? Der Sinn von Properties ist, dass man als Entwickler der Klasse sehr leicht die Implementierung der Property ändern kann (zum Beispiel von nem Feld zu nem Getter) ohne, dass es der Verwender großartig bemerkt (natürlich generiert der Compiler dann anderen Code, aber der Quelltext ändert sich nicht). Bei solchen automatisch generierten Gettern käme dann auf einmal ein neuer Exceptiontyp hinzu, mit dem man nicht unbedingt rechnet, vielleicht nicht nur als Benutzer nicht, sondern auch als Entwickler nicht.
Nil
Noch eine weitere Anmerkung zum Schluss: wer vor dem Performanceoverhead durch den Getter "Angst" hat, der soll doch einfach Inline verwenden:
Delphi-Quellcode:
(Hinweis: Kompilat nicht ausführen, da
program tinlinegetter;
{$ifdef fpc} {$mode objfpc} {$endif} type TTestClass1 = class Foo: LongInt; end; TTestClass2 = class Bar: TTestClass1; function GetFoo: LongInt; inline; property Foo: LongInt read GetFoo; end; function TTestClass2.GetFoo: LongInt; begin Result := Bar.Foo; end; var t: TTestClass1; i: LongInt; begin i := t.Foo; end.
Delphi-Quellcode:
und
t
Delphi-Quellcode:
nicht initialisiert sind!)
Bar
Zumindest FPC optimiert hier durch das
Delphi-Quellcode:
den gesamten Call weg:
inline
Code:
Im Vergleich dazu der generierte Code ohne
.section .text
.balign 16,0x90 .globl PASCALMAIN .type PASCALMAIN,@function PASCALMAIN: .globl main .type main,@function main: .Lc6: # Temps allocated between ebp+0 and ebp+0 # [26] begin pushl %ebp .Lc8: .Lc9: movl %esp,%ebp .Lc10: call FPC_INITIALIZEUNITS # [27] i := t.Foo; movl U_P$TINLINEGETTER_T,%eax movl 4(%eax),%eax movl 4(%eax),%eax movl %eax,U_P$TINLINEGETTER_I # [28] end. call FPC_DO_EXIT leave ret .Lc7: .Le1: .size main, .Le1 - main
Delphi-Quellcode:
inline
Code:
Gruß,
.section .text
.balign 16,0x90 .globl PASCALMAIN .type PASCALMAIN,@function PASCALMAIN: .globl main .type main,@function main: .Lc6: # Temps allocated between ebp+0 and ebp+0 # [26] begin pushl %ebp .Lc8: .Lc9: movl %esp,%ebp .Lc10: call FPC_INITIALIZEUNITS # [27] i := t.Foo; movl U_P$TINLINEGETTER_T,%eax call P$TINLINEGETTER_TTESTCLASS2_$__GETFOO$$LONGINT movl %eax,U_P$TINLINEGETTER_I # [28] end. call FPC_DO_EXIT leave ret .Lc7: .Le1: .size main, .Le1 - main Sven |
AW: Warum geht das eigentlich nicht?
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:58 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-2025 by Thomas Breitkreuz