AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Programmieren allgemein Delphi Warum geht das eigentlich nicht?
Thema durchsuchen
Ansicht
Themen-Optionen

Warum geht das eigentlich nicht?

Ein Thema von Mavarik · begonnen am 19. Jul 2014 · letzter Beitrag vom 20. Jul 2014
Antwort Antwort
Seite 2 von 2     12   
Dejan Vu
(Gast)

n/a Beiträge
 
#11

AW: Warum geht das eigentlich nicht?

  Alt 20. Jul 2014, 10:17
Interessanterweise geht sowas aber mit einem Record, wenn man beim read wieder ein Feld des Records anspricht. Und mit privat oder nicht hat das auch nichts zu tun.
Delphi-Quellcode:
..
public
  fFoo : Integer;
  Property Foo : Integer read fFoo;
end;
ist zwar syntaktisch korrekt aber behämmert. Die Variante
Delphi-Quellcode:
..
protected
  fFoo : Integer;
public
  Property Foo : Integer read fFoo;
end;
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.

Bei deinem interessanten Beispiel bei einem Record meinst Du das hier?
Delphi-Quellcode:
Type
  TRec1 = Record
    Foo : Integer;
  End;
  TRec = Record
    Rec1 : TRec1;
    Property Foo : Integer Read Rec1.Foo;
  End;
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
Delphi-Quellcode:
Type
  TRec1 = class
    Foo : Integer;
  End;
  TRec = class
    Rec1 : TRec1;
    Property Foo : Integer Read Rec1.Foo;
  End;
Zitat von Compiler:
[Pascal Fehler] Unit16.pas(28): E2467 Record oder Objekttyp erforderlich
Ein Record geht also auch (=direktes Mapping)
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie
Online

Registriert seit: 12. Aug 2003
Ort: Soest
4.019 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#12

AW: Warum geht das eigentlich nicht?

  Alt 20. Jul 2014, 11:28
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?
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
Benutzerbild von JamesTKirk
JamesTKirk

Registriert seit: 9. Sep 2004
Ort: München
604 Beiträge
 
FreePascal / Lazarus
 
#13

AW: Warum geht das eigentlich nicht?

  Alt 20. Jul 2014, 12:24
Weil der backing store ein privates Feld sein muss und nicht die Property eines privaten Feldes.
Interessanterweise geht sowas aber mit einem Record, wenn man beim read wieder ein Feld des Records anspricht. Und mit privat oder nicht hat das auch nichts zu tun.

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.
Der Unterschied zwischen der Verwendung eines record (oder auch eines object ) 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 TObject.InstanceSize verwendet:

Delphi-Quellcode:
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.
Ausgabe:

Code:
TTestClass1: 32
TTestClass2: 8
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:

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.
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?
Der Compiler könnte das durchaus machen, doch beim Zugriff auf einen Klassenzeiger ergibt sich ein Problem, dass sich bei einem Record nicht gibt: was, wenn der Klassenzeiger Nil 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.

Noch eine weitere Anmerkung zum Schluss: wer vor dem Performanceoverhead durch den Getter "Angst" hat, der soll doch einfach Inline verwenden:

Delphi-Quellcode:
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.
(Hinweis: Kompilat nicht ausführen, da t und Bar nicht initialisiert sind!)

Zumindest FPC optimiert hier durch das inline den gesamten Call weg:

Code:
.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
Im Vergleich dazu der generierte Code ohne inline
Code:
.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
Gruß,
Sven
Sven
[Free Pascal Compiler Entwickler]
this post is printed on 100% recycled electrons
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie
Online

Registriert seit: 12. Aug 2003
Ort: Soest
4.019 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#14

AW: Warum geht das eigentlich nicht?

  Alt 20. Jul 2014, 14:13
Der Compiler könnte das durchaus machen, doch beim Zugriff auf einen Klassenzeiger ergibt sich ein Problem, dass sich bei einem Record nicht gibt: was, wenn der Klassenzeiger Nil 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.
Und wo ist dann der Unterschied zu dem 0815 Getter, den wahrscheinlich die meisten an solch einer Stelle schreiben würden? (so wie du in dem untersten Beispiel) Wenn ich nen nilsafen Getter haben will, dann kann ich ihn immernoch selbst schreiben, aber für die Fälle wo ich bloß eine Property oder eine Funktion eines aggregierten Objekts nach außen leiten möchte könnt ich mir ein halbes Duzend Zeilen Boilerplate Code sparen.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:49 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