![]() |
Instanz einer Klasse identifizieren?!
Hallo
Um mein Problem/Anliegen zu erläutern werde ich dies an einem Kleinen Beispiel machen. Ich habe die Unit "UTest.pas", in der folgende Typen-Deklaration ist:
Delphi-Quellcode:
Später im Programm habe ich zum Beispiel einen Button1 mit der OnClick-Prozedur
...
type TTest = class private public Text: string; constructor Create(ID: integer); end; ...
Delphi-Quellcode:
Und nun möchte ich zum Beispiel auf einem 2ten ButtonClick die erzeugte Instanz "wiederfinden". Zum Beispiel:
procedure TForm1.Button1Click(Sender: TObject);
var hallo: TTest; begin hallo:= TTest.Create(22); end;
Delphi-Quellcode:
Wie kann ich dies ganze bewerkstelligen ohne eine globale Variable zu verwenden. Das ganze sieht ja ganz stark nach einem Array aus, welches ich allerdings nicht global haben möchte.
procedure TForm1.Button2Click(Sender: TObject);
begin TTest[22].Text:= 'Irgendwas'; end; Ist es irgendwie möglich ein solches Array mit in die Unit Utest.pas zu packen? Vielen Dank! |
Re: Instanz einer Klasse identifizieren?!
Besser wäre da eine TObjectList bzw. ein spezialiserter Nachfahre dieser.
Wenn Du die Liste nur in der Unit brauchst, kannst Du sie ja lokal deklarieren. |
Re: Instanz einer Klasse identifizieren?!
Hi,
wie kommt denn die "22" zustande ? Zählst Du bei jedem Button1Click weiter ? Willst Du die jeweils letzte Instanz haben ? Nach welchen Kriterien soll den Button2Click die Instanz auswählen ? |
Re: Instanz einer Klasse identifizieren?!
Sorry, ich habe vergessen dies zu kommentieren.
Die 22 ist einfach ne Beliebige ID. Das heisst jede Instanz soll am besten mit einer ID versehen werden an der man die Klasse dann identifizieren kann. Zum Beispiel könnte ich mir auch sowas vorstellen:
Delphi-Quellcode:
Ich weiß allerdings nicht ob das geht.
TTest[22].Text = 'blub'
Ich werde mir auch nochmal TObjectList anschauen, eventuell bekomme ich es damit hin ... |
Re: Instanz einer Klasse identifizieren?!
Hi,
wenn du wirklich gänzlich auf globale Variablen verzichten möchtest, kannst du eine Kapselung bauen, die dir Instanzen liefert. Ich weiß nicht in wie weit du mit dem Factory-Pattern vertraut bist, aber etwas in der Art kann dir hier schon sehr gut weiterhelfen. Erstelle einfach eine Klasse, die alle Objekte von deinem Typen verwaltet. (hier könntest du dann auch automatische IDs vergeben). Jedenfalls definierst du hier auch eine Methode, die dir eine Instanz liefert (und diese Instanz speichert, ob im Array oder in einer Liste ist dann deine Sache). Über genau diese kannst du dann auch alle Instanzen finden (die deine persönlichen Bedingungen erfüllen). Ok, weicht damit schon stark vom eigentlichen Sinn des Factory-Pattern ab, aber das wäre eine andere Sache. Gruß Der Unwissende |
Re: Instanz einer Klasse identifizieren?!
Das Factory-Pattern wollte ich ihm auch vorschlagen, nur das endet letztendlich mit der beschriebenen Erweiterung des patterns auch in einer globalen Variable. Wo soll die Faktory sonst die Objekte verwalten?
Hier mal ein Beispiel einer typisierten ObjectList mit Erweiterung:
Delphi-Quellcode:
TTest = class
private ID : Integer; public Text: string; constructor Create(_ID: integer); end; TTestObjectList = class(TObjectList) protected function GetItem(Index: Integer): TTest; procedure SetItem(Index: Integer; AItem: TTest); public function Add(AItem: TTest): Integer; function Extract(Item: TTest): TTest; function Remove(AItem: TTest): Integer; function IndexOf(AItem: TTest): Integer; procedure Insert(Index: Integer; AItem: TTest); function First: TTest; function Last: TTest; property Items[Index: Integer]: TTest read GetItem write SetItem; default; function GetItemById(_ID : Integer; _CreateIfNotExists : Boolean = true) : TTest; end; { TTest } constructor TTest.Create(_ID: integer); begin ID := _ID; end; { TTestObjectList } function TTestObjectList.Add(AItem: TTest): Integer; begin Result := inherited Add(AItem); end; function TTestObjectList.Extract(Item: TTest): TTest; begin Result := TTest(inherited Extract(Item)); end; function TTestObjectList.First: TTest; begin Result := TTest(inherited First); end; function TTestObjectList.GetItem(Index: Integer): TTest; begin Result := TTest(inherited Items[Index]); end; function TTestObjectList.IndexOf(AItem: TTest): Integer; begin Result := inherited IndexOf(AItem); end; procedure TTestObjectList.Insert(Index: Integer; AItem: TTest); begin inherited Insert(Index, AItem); end; function TTestObjectList.Last: TTest; begin Result := TTest(inherited Last); end; procedure TTestObjectList.SetItem(Index: Integer; AItem: TTest); begin inherited Items[Index] := AItem; end; function TTestObjectList.Remove(AItem: TTest): Integer; begin Result := inherited Remove(AItem);end; function TTestObjectList.GetItemById(_ID: Integer; _CreateIfNotExists: Boolean): TTest; var i : Integer; begin Result := nil; for i := 0 to Count-1 do if Items[i].ID = _ID then begin Result := Items[i]; exit; end; if not _CreateIfNotExists then exit; Result := TTest.Create(_ID); Add(Result); end; |
Re: Instanz einer Klasse identifizieren?!
willst du nur keine Globale nehmen oder auch nix im Private/Public/Protected definieren? Und was spricht dagegen das am private/public/protected zu definieren, dafür sind die Bereiche doch da. Ganz richtig wäre es natürlich wenn du eine Liste baust welche die instanzen verwaltet dann musst du diese Liste aber auch im Private/Public/Protected halten.
|
Re: Instanz einer Klasse identifizieren?!
Eine einfache Implementation einer prozessbezogenen statischen Klassenvariablen mit automatischer Generierung von IDs (warum sollte der Benutzer der Klasse diese generieren müssen...) könnte so aussehen:
Delphi-Quellcode:
{ interface }
type TFooInstanceID = Integer; TFoo = class private FInstanceID: TFooInstanceID; public constructor Create(); virtual; property InstanceID: TFooInstanceID read FInstanceID; end; { implementation } var // Prozessbezogene statische Klassenvariable von TFoo _FooInstanceID: TFooInstanceID; // = 0 constructor TFoo.Create(); begin inherited Create(); FInstanceID := InterlockedIncrement(_FooInstanceID); end; |
Re: Instanz einer Klasse identifizieren?!
Ok, statische Klassenvariablen gibt es ja eigentlich nicht in Delphi. Natürlich ist deine Variable, die du hier hochzählst wiederum global und das lässt sich (wie hier schon gesagt wurde) einfach vermeiden.
Was ich meinte noch mal als Delphi Code
Delphi-Quellcode:
type
TFoo = class(TObject) private FId : Cardinal; ... public constructor create(const Id : Cardinal); property Id : Cardinal read FId; end; constructor TFoo.Create(const Id : Cardinal); begin inherited create; self.Id := Id; end;
Delphi-Quellcode:
Wie man sieht, kann man also sehr leicht eine Liste im Private Bereich verwalten und imho gehört sie auch genau dahin. Dies ist natürlich ein stark vereinfachtes Beispiel, aber es kann sehr leicht angepasst werden, so dass man dann die TFooList nur noch nach einem speziellen TFoo durchsucht.
type
TFooFactory = class(TObject) private id : Cardinal; InstanceList : TFooList; public constructor create; destructor destroy; override; function getFooInstance : TFoo; end; constructor TFooFactory.Create; begin inherited create; self.id := 0; self.InstanceList := TFooList.Create; end; destructor TFooFactory.Destroy; begin self.InstanceList.Free; // Achtung, hier müssen natürlich auch alle gespeicherten Instanzen aut. gelöscht werden inherited destroy; end; function TFooFactory.getFooInstance : TFoo; var buffer : TFoo; begin buffer := TFoo.Create(self.id); inc(self.id); self.InstanceList.Add(buffer); end; Gruß Der Unwissende [Edit] Ach ja, wenn man die Nummern alle nur einmal vergeben möchte, übergreifend für mehr als einen möglichen Aufrufer, sollte man natürlich etwas threadsafe arbeiten. Ansonsten natürlich auch noch ein Singleton-Pattern auf die Factory anwenden, dann gibt es nur eine Instanz von dieser TFooFactory, die man bekommt und somit hat man dort auch wieder eine globale Eindeutigkeit der Ids. Allerdings kommt man beim Singleton Pattern (soviel ich weiß) nicht mehr um eine globale Variable herum. [/Edit] |
Re: Instanz einer Klasse identifizieren?!
Zitat:
Nehmen wir zum Beispiel die GetInstance() einer möglichen Singleton-Implementation:
Delphi-Quellcode:
Durch die Ausnutzung von {$WRITEABLECONST} sind die globalen Variablen nur innerhalb der Funktion sichtbar (und können nicht 'versehentlich' von 'außen' modifiziert werden).
class function TFoo.GetInstance(): TFoo;
{$WRITEABLECONST ON} const InstanceLock: Integer = Ord(False); Instance: TFoo = nil; {$WRITEABLECONST OFF} begin Result := Instance; if not Assigned(Result) then begin // Wait until lock is available while InterlockedExchange(InstanceLock, Ord(True)) = Ord(True) do Sleep(0); try // Create Instance if needed if not Assigned(Instance) then Instance := TFoo.Create(); Result := Instance; finally // Release the lock InterlockedExchange(InstanceLock, Ord(False)); end; end; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:36 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