Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen (https://www.delphipraxis.net/198478-zugriff-auf-gemeinsame-eigenschaft-unterschiedlichen-klassen.html)

norwegen60 6. Nov 2018 14:03

Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
Hallo zusammen,

ich suche eine Möglichkleit wie ich in einer Klassenstruktur auf ein beliebiges Parent-Object zugreifen kann.

Ich habe beispielhaft folgende Klassenstruktur
Delphi-Quellcode:
  TBase = class
  private
    FNr: Integer;
    FParentObject: TObject;
  public
    property Nr: Integer read FNr write FNr;
    property ParentObject: TObject read FParentObject write FParentObject;
  end;
 
  TMessData1 = class(TBase)
  private
    FMessdata1: Real;
  public
    property FMessdata1: Realread FMessdata1 write FMessdata1;
  end;

  TMessData1List = class(TObjectList<TMessData1>)
  private
    FParentObject: TObject;
  public
    property ParentObject: TObject read FParentObject write FParentObject;
  end;

  TMessData2 = class(TBase)
  private
    FMessdata2: Real;
    FMessDataList1: TMessData1List;
  public
    property FMessdata2: Realread FMessdata2 write FMessdata2;
  end;

  TMessData2List = class(TObjectList<TMessData2>)
  private
    FParentObject: TObject;
  public
    property ParentObject: TObject read FParentObject write FParentObject;
  end;

  TGeraet = class(TBase)
  private
    FBezeichnung: String;
    FMessDataList2: TMessData2List;
  public
    property Bezeichnung: Realread FBezeichnung write FBezeichnung;
  end;
Die Struktur sieht also so aus
Code:
TGeraet
    - TMessDataList2
        - MessData2_1
            - TMessDataList1
                - MessData1_1
                - MessData1_2
                - MessData1_n
        - MessData2.2
            - TMessDataList1
                - MessData1_11
                - MessData1_12
                - MessData1_1n
        - MessData2.n
            - TMessDataList1
                - MessData1_21
                - MessData1_22
                - MessData1_2n
Da in allen Elementen die Eigenschaft ParentObject vorhanden ist, ist es grundsätzlich kein Problem z.B. aus MessData1_2n über
Delphi-Quellcode:
Geraet = TMessDataList2(TMessData2(TMessDataList1(ParentObject).ParentObject).ParentObject


oder über die Basisklasse
Delphi-Quellcode:
Geraet = TMessDataList2(TBase(TMessDataList1(ParentObject).ParentObject).ParentObject

Ich suche aber eine Möglichkeit, wo ich die Struktur nicht kennen muss sondern über eine Schleife so lang über ParentObject nach oben klettere bis ich die Klasse TGeraet erreicht habe. Ohne die Listen dazwischen würde es über die Basisklasse TBase gehen. Wie aber komme ich in der TObjectList weiter hoch?

Ich hoffe ich habe mich verständlich ausgedrückt. Hintergrund ist, dass die Klassenstruktur etwas komplexer ist. Z.B. könnte das Konstrukt TMessDataList1 auch direkt in TGeraet hängen.

Vielen Dank
Gerd

PS: Die Idee, in ParentObject nicht die Liste sondern die darüber liegende Klasse anzugeben, nutzt nichts. Es könnte auch sein, dass ich nicht TGeraet sondern TMessData2List suche.
-

stahli 6. Nov 2018 14:08

AW: Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
Du solltest Deine Listen auch von TBase ableiten und die eigentliche Liste dort nur intern benutzen.
TBaseList wäre dann ein Wrapper um eine normale Liste.

norwegen60 6. Nov 2018 14:33

AW: Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
Hallo Stahli,

daran habe ich auch schon gedacht. Wie kann ich dann aber beim Hochblättern feststellen ob ich in TMessData1List oder TMessData2List bin. Typabfrage funktioniert dann ja nicht mehr. Oder funktioniert ein Konstrukt wie
Delphi-Quellcode:
TBaseList = class(TObjectList<TBase>)
  privat
    FParentObject: TObject;
  public
    property ParentObject: TObject read FParentObject write FParentObject;
  end;

TMessData1List = class(TBaseList)
  privat
  public
  end;

hoika 6. Nov 2018 14:36

AW: Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
Benutz Interfaces

norwegen60 6. Nov 2018 14:40

AW: Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
Zitat:

Zitat von hoika (Beitrag 1417523)
Benutz Interfaces

Mit denen habe ich mich noch nie abgegeben. Könntest du anzeigen, wie das geht. Meine Bedenken sind bei so was, dass es zuerst ganz einfach aussieht und man dann feststellt, dass man vieles beachten muss, was man noch nicht weiß. Ich bin ziemlich unter Zeitdruck

stahli 6. Nov 2018 15:23

AW: Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
Interfaces bringen jetzt an der Stelle nicht wirklich viel.

Ich würde auch grundsätzlich zu Interfaces raten, aber man muss sich erst damit befassen. Die Klassen muss man dann aber auch differenziert ausgestalten.

Ich würde es so machen:

Delphi-Quellcode:
  TBase = class
   private
     FNr: Integer;
     FParentObject: TObject;
   public
     property Nr: Integer read FNr write FNr;
     property ParentObject: TObject read FParentObject write FParentObject;
   end;
   
   TMessData1List = class(TBase)
   private
     fItems: (TObjectList<TMessData1>);
     function get_Item(const Index: Integer): TMessData1;
     procedure set_Item(const Index: Integer; Value: TMessData1);
   public
     procedure Add(Item: TMessData1);
     procedure Delete(Item: TMessData1); overload;
     procedure Delete(Index: Integer); overload; // alle Methoden, die für die Liste gebraucht werden
   protected
   public
     property Items[const Index: Integer]: TMessData1 read get_Item write set_Item; // weiß nicht, ob die Syntax so richtig ist
   end;

norwegen60 6. Nov 2018 15:48

AW: Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
Hallo Stahli,

ja, das sieht gut aus. Bedeutet aber auch einiges an Umstellarbeit. Die würde ich gerne verschieben. Deshalb die Frage ob mein Ansatz funktioniert?
Delphi-Quellcode:
TBaseList = class(TObjectList<TBase>)
  privat
    FParentObject: TObject;
  public
    property ParentObject: TObject read FParentObject write FParentObject;
  end;

TMessData1List = class(TBaseList)
  privat
  public
  end;
Oder habe ich da ein Gedankenfehler?

Danke
Gerd

stahli 6. Nov 2018 16:26

AW: Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
Jetzt ohne Delphi kann ich gar nicht sagen, ob das compiliert (ich würde vermuten, dass nicht).
Wenn es compiliert müsstest Du immer prüfen, von welcher Klasse das jeweilige ParentObjekt ist. Du musst also immer mit is und as casten.


Mein Ansatz ist nicht so aufwendig, wie man zunächst vermuten würde.
Du musst nur eine neu benötigte Listenmethode neu implementieren, wenn Du sie erstmalig brauchst. So schlimm ist das nicht.

Außerdem könntest Du auch Dein Parent als ParentObject als TBase verwalten.

Delphi-Quellcode:
FParentObject: TTBase;
Dann kannst Du immer weiter die Parents durchlaufen und musst nicht unterscheiden, was das für ein tatsächliches Objekt ist. Es muss immer nur von TBase abgeleitet sein.

norwegen60 6. Nov 2018 16:57

AW: Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
Kompilieren lässt es sich und das mit dem Cast ist bekannt. Meine Erfahrung ist nur dass sich die Probleme erst später zeigen. Drum meine Rückfrage ob es etwas gibt, was dagegen spricht.

Die Listen werden jetzt schon recht intensiv genutzt. Umgestellung ist vielleicht wirklich überschaubar. Ich scheue (noch) den Testaufwand.

Bei deinem Ansatz FParentObject: TBase bin ich nicht ganz sicher. Darf bei der Deklaration von TBase eine Eigenschaft auf sich selber zeigen. Oder muss ich dann och eine Klasse davor setzen?
Grüße

stahli 7. Nov 2018 09:23

AW: Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
... darf es. :-)

norwegen60 9. Nov 2018 19:02

AW: Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
Zitat:

Zitat von stahli (Beitrag 1417527)
Ich würde es so machen:

Delphi-Quellcode:
  TBase = class
   private
     FNr: Integer;
     FParentObject: TObject;
   public
     property Nr: Integer read FNr write FNr;
     property ParentObject: TObject read FParentObject write FParentObject;
   end;
   
   TMessData1List = class(TBase)
   private
     fItems: (TObjectList<TMessData1>);
     function get_Item(const Index: Integer): TMessData1;
     procedure set_Item(const Index: Integer; Value: TMessData1);
   public
     procedure Add(Item: TMessData1);
     procedure Delete(Item: TMessData1); overload;
     procedure Delete(Index: Integer); overload; // alle Methoden, die für die Liste gebraucht werden
   protected
   public
     property Items[const Index: Integer]: TMessData1 read get_Item write set_Item; // weiß nicht, ob die Syntax so richtig ist
   end;

Jetzt wollte ich das mal ausprobieren und beim Durchspielen stellte ich fest, dass entweder du oder ich einen Gedankenfehler haben.
Problem: Wenn ich auf einem der Einträge MessData1 stehe muss ich ja wieder zuerst durch die Liste um zu wissen zu wem die Liste gehört. Jetzt könnte ich natürlich MessData1 als ParentObject TMessData1List zuordnen, aber wie schon gesagt, kann es auch sein dass ich nicht eine Class finden will sondern eine darüber liegende Liste.

Oder sehe ich was nicht?

Grüße
Gerd

norwegen60 9. Nov 2018 19:39

AW: Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
Zitat:

Zitat von norwegen60 (Beitrag 1417522)
Delphi-Quellcode:
TBaseList = class(TObjectList<TBase>)
  privat
    FParentObject: TObject;
  public
    property ParentObject: TObject read FParentObject write FParentObject;
  end;

TMessData1List = class(TBaseList)
  privat
  public
  end;

Auch dieses Konstrukt hat einen großen Nachteil. Was vorher so aussah
Delphi-Quellcode:
var
  lBaseSample: TBaseSample;
begin
  Result := false;
  for lBaseSample in FSamplingList do
    if (lBaseSample.StatusSample <> assFilled) and (lBaseSample.StatusSample <> assProcessed) then
      exit;
  Result := true;
end;
sieht jetzt so aus:
Delphi-Quellcode:
var
  i: Integer;
  lBaseSample: TBaseSample;

begin
  Result := true;
  for i := 0 to Pred(FSamplingList.count) do
  begin
    lBaseSample := TBaseSample(FSamplingList[i]);
    if (lBaseSample.StatusSample <> assFilled) and (lBaseSample.StatusSample <> assProcessed) then
      exit;
  end;
  Result := true;
end;
Was mich am meisten daran stört ist dass ich jetzt überall ein Typwandlung
Delphi-Quellcode:
lBaseSample := TBaseSample(FSamplingList[i]);
statt
Delphi-Quellcode:
lBaseSample := FSamplingList[i];
machen muss.

Jetzt wäre ich doch froh, wenn mir noch mal einer einen Tipp geben könnte

Grüße
Gerd

stahli 10. Nov 2018 11:27

AW: Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
Zitat:

Zitat von norwegen60 (Beitrag 1417768)
Wenn ich auf einem der Einträge MessData1 stehe muss ich ja wieder zuerst durch die Liste um zu wissen zu wem die Liste gehört. Jetzt könnte ich natürlich MessData1 als ParentObject TMessData1List zuordnen, aber wie schon gesagt, kann es auch sein dass ich nicht eine Class finden will sondern eine darüber liegende Liste.

Ich sehe da jetzt nicht das Problem.
Du könntest in der Basisklasse eine Suchfunktion, die dann nach oben iteriert:

Delphi-Quellcode:
TBase = class
private
  FNr: Integer;
  FParentObject: TBase;
public
  function FoundParentClass(aClassName: String): TBase;
  function FoundParentItem(aItem: TBase): Boolen;
  property Nr: Integer read FNr write FNr;
  property ParentObject: TBase read FParentObject write FParentObject;
end;


function TBase.FoundParentClass(aClassName: String): TBase;
begin
  Result := nil;
  if Assigned(ParentObject) then
  begin
    if (ParentObject.ClassName = aClassName) then
      Exit(ParentObject);
    Result := ParentObject.FoundParentClass(aClassName);
  end;
end;

function TBase.FoundParentItem(aItem: TBase): Boolen;
begin
  Result := False;
  if Assigned(ParentObject) then
  begin
    if (ParentObject = aItem) then
      Exit(True);
    Result := ParentObject.FoundParentItem(aItem);
  end;
end;
So kannst Du dich von einer beliebigen Stelle aus weiter nach oben hangeln.
Ob die Listeneinträge die Liste als ihren Parent sehen sollen, musst Du abhängig von der Programmlogik entscheiden.

Zitat:

Zitat von norwegen60 (Beitrag 1417774)
Auch dieses Konstrukt hat einen großen Nachteil. Was vorher so aussah
[DELPHI] lBaseSample := TBaseSample(FSamplingList[i]);

Das ist aber auch nicht mein Vorschlag.


Kannst Du mal ein Unit hochladen, in der diese Klassen vollständig enthalten sind?
Es bräuchte ja keine GUI vorhanden sein - nur eine kompilierbare Unit. Dann könnte man (ich?) die mal entsprechend umbauen.

norwegen60 11. Nov 2018 12:01

AW: Zugriff auf gemeinsame Eigenschaft in unterschiedlichen Klassen
 
Hallo Stahli,

zuerst mal Danke für deine Unetrstützung.

Zitat:

Zitat von stahli (Beitrag 1417798)
Delphi-Quellcode:
TBase = class
private
  FNr: Integer;
  FParentObject: TBase;
public
  function FoundParentClass(aClassName: String): TBase;
  function FoundParentItem(aItem: TBase): Boolen;
  property Nr: Integer read FNr write FNr;
  property ParentObject: TBase read FParentObject write FParentObject;
end;

function TBase.FoundParentItem(aItem: TBase): Boolen;
begin
  Result := False;
  if Assigned(ParentObject) then
  begin
    if (ParentObject = aItem) then
      Exit(True);
    Result := ParentObject.FoundParentItem(aItem);
  end;
end;

Ich glaube ich weiß wo mein Denkfehler liegt. Ich hatte bei MessData1 als Parent weiterhin die Liste (FItems) eingetragen. Damit musste ich natürlich wieder durch diese Liste. Sobald ich aber sage MessData1.ParentObject := MessData1List mit MessDataList = TMessDataList =class(TBase) sollte es gehen.


Zitat:

Zitat von stahli (Beitrag 1417798)
Das ist aber auch nicht mein Vorschlag.

Ich weiß. Ich will nur verstehen und habe auch da mal mit meinem Ansatz weiter getestet.


Zitat:

Zitat von stahli (Beitrag 1417798)
Kannst Du mal ein Unit hochladen, in der diese Klassen vollständig enthalten sind?
Es bräuchte ja keine GUI vorhanden sein - nur eine kompilierbare Unit. Dann könnte man (ich?) die mal entsprechend umbauen.

Der Code ist in 20 Jahren gewachsen und wir sind aktuell daran, ihn aufzuräumen. Noch aber sind viel zu viele Abhängigkeiten da. Wenn ich nicht weiter komme, versuche ich mal was zu extrahieren.

Ich hatte noch folgenden Ansatz:
Delphi-Quellcode:
  TMyObjectList<T> = class(TObjectList<T>)
  private
    FParentObject: TBase;
  public
    property ParentObject: Integer read FParentObject write FParentObject;
  end;

  TBaseList = class(TMyObjectList<TBase>);
 
  // Und die Suche nach einer bestimmten Klasse, z.B. TAnalysis
  lObject := ParentObject;
  repeat
    if lObject is TAnalysis then              // gefunden
      Result := TAnalysis(lObject)
    else if ParentObject is TBase then        // Wenn TBase eine Klasse höher
      lObject := TBase(lObject).ParentObject
    else if ParentObject is TBaseList then    // Wenn Liste eine Klasse höhr
      lObject := TBaseList(lObject).ParentObject
    else
      lObject := nil;
  until (Result <> nil) or (lObject = nil);
Der code
Delphi-Quellcode:
else if ParentObject is TBaseList then
      lObject := TBaseList(lObject).ParentObject
funktioniert aber nicht nicht. Geht das irgendwie?


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:35 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