Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi class helper (https://www.delphipraxis.net/172414-class-helper.html)

Helmi 2. Jan 2013 13:25

class helper
 
Hallo,

ich setz zum ersten mal einen class helper ein und hab dazu eine Frage ob das zu Problemen kommen kann oder nicht.

Ich habe eine ListBox auf der Form liegen und möchte nun die Klasse TListBox in der Unit um eine Variable erweitern.

Ich hab nun folgendes stehen:
Delphi-Quellcode:
type
  TListBoxHelper = class helper for TListBox
     class var
       aktive_Datei: String;
     end;

  TMainForm = class(TForm)
  {...}
Es funktioniert - ich habe die Variable nun zur Verfügung bei einer ListBox-Instanz und kann diese auch problemlos nutzen.
Nur kann es so zu Problemen kommen? - oder ist das genau für so was gedacht gewesen?

romankassebaum 2. Jan 2013 13:29

AW: class helper
 
Hallo,

falls Du die Variable für eine Instanz nutzen möchtest, dann solltest Du ein Feld und keine Klassenvariable nutzen.

--
Roman Kassebaum

Helmi 2. Jan 2013 13:37

AW: class helper
 
Zitat:

Zitat von romankassebaum (Beitrag 1197344)
Hallo,

falls Du die Variable für eine Instanz nutzen möchtest, dann solltest Du ein Feld und keine Klassenvariable nutzen.

wie meinst du das?
ohne class var gehts nicht

DeddyH 2. Jan 2013 13:39

AW: class helper
 
Interessanter Artikel dazu: http://stackoverflow.com/questions/2...-class-helpers, besonders der Beitrag von Uwe Raabe.

Helmi 2. Jan 2013 13:53

AW: class helper
 
Zitat:

Zitat von DeddyH (Beitrag 1197346)
Interessanter Artikel dazu: http://stackoverflow.com/questions/2...-class-helpers, besonders der Beitrag von Uwe Raabe.

Danke für den Link - aber irgendwie bringt mich der nicht weiter (oder ich seh es nicht)

Dort sind im class helper "nur" procedures und functions deklariert - aber keine einfache Variable.

Wenn ich die Variable einfach so definiere:
Delphi-Quellcode:
  TListBoxHelper = class helper for TListBox
    private
      aktive_Datei: String;
   end;
gibts diesen Fehler:
Zitat:

[DCC Fehler] MainUnit.pas(40): E2169 Felddefinition nicht erlaubt nach Methoden oder Eigenschaften
bislang gehts nur so:
Delphi-Quellcode:
     class var
        aktive_Datei: String;
      end;
versteh ich grad etwas falsch?
(dummerweise ist die Delphi-Hilfe nicht wirklich eine Hilfe dabei)

Uwe Raabe 2. Jan 2013 13:56

AW: class helper
 
Class Helper können keine zusätzlichen Felder für Instanzen bereitstellen! Es sind lediglich Methoden erlaubt.

Helmi 2. Jan 2013 13:59

AW: class helper
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1197348)
Class Helper können keine zusätzlichen Felder für Instanzen bereitstellen! Es sind lediglich Methoden erlaubt.

also dann ist
Delphi-Quellcode:
class var
die einzige Lösung für mein "Problem"?

stahli 2. Jan 2013 14:01

AW: class helper
 
Allister Christie hatte mal ein nettes Video dazu veröffentlicht: http://www.codegearguru.com/video/029/ClassHelpers.html

Im Grunde kann man damit Dinge wie in normalen Funktionen regeln, nur dass der Compiler so tut, als würde die Funktion zur Klasse gehören.

Statt
Delphi-Quellcode:
X := GetResult(MyClass);
kannst Du
Delphi-Quellcode:
X := MyClass.GetResult;
schreiben.
Die Funktion GetResult kann aber nur auf öffentliche Eigenschaften der Klasse zugreifen. Man kann der Klasse KEINE Variablen hinzufügen.

(Dass class var geht, hätte ich gar nicht gewusst/gedacht.)

DeddyH 2. Jan 2013 14:01

AW: class helper
 
In dem Fall muss man sich mit einer Cracker-Klasse behelfen, da geht das dann wieder.
Delphi-Quellcode:
uses Windows, SysUtils, {...,} StdCtrls;

type
  TListbox = class(StdCtrls.TListbox)
  private
    FAktive_Datei: string;
  public
    property Aktive_Datei: string read FAktive_Datei write FAktive_Datei;
  end;

  TDeinForm = class(TForm)
    DeineListbox: TListbox; //das ist dann die gerade definierte
    ...
  end;

Helmi 2. Jan 2013 14:05

AW: class helper
 
Zitat:

Zitat von DeddyH (Beitrag 1197352)
In dem Fall muss man sich mit einer Cracker-Klasse behelfen, da geht das dann wieder.
Delphi-Quellcode:
uses Windows, SysUtils, {...,} StdCtrls;

type
  TListbox = class(StdCtrls.TListbox)
  private
    FAktive_Datei: string;
  public
    property Aktive_Datei: string read FAktive_Datei write FAktive_Datei;
  end;

  TDeinForm = class(TForm)
    DeineListbox: TListbox; //das ist dann die gerade definierte
    ...
  end;

Daran hab ich auch schon gedacht! - Dann ergibt sich für mich nicht wirklich der sinn von class helper
aber das ist eine andere Diskussion...

Helmi 2. Jan 2013 14:21

AW: class helper
 
Zitat:

Zitat von stahli (Beitrag 1197351)
Allister Christie hatte mal ein nettes Video dazu veröffentlicht: http://www.codegearguru.com/video/029/ClassHelpers.html

Das Video ist echt gut...

romankassebaum 2. Jan 2013 14:42

AW: class helper
 
Hallo,

um noch einmal auf meine erste Antwort zurückzukommen: Es wird eine Variable pro Instanz benötigt. Klassenvariablen können das nicht leisten. Also muss man sich etwas anderes überlegen.
Ich persönlich würde wahrscheinlich mit einem Feld arbeiten. Da Klassenhelper das nicht können, würde ich ableiten oder mir das Feld in dem Formular merken. Das kommt auf den konkreten Fall an.

--
Roman Kassebaum

Helmi 2. Jan 2013 14:51

AW: class helper
 
Zitat:

Zitat von romankassebaum (Beitrag 1197360)
...
um noch einmal auf meine erste Antwort zurückzukommen: Es wird eine Variable pro Instanz benötigt. Klassenvariablen können das nicht leisten. Also muss man sich etwas anderes überlegen.
Ich persönlich würde wahrscheinlich mit einem Feld arbeiten. Da Klassenhelper das nicht können, würde ich ableiten oder mir das Feld in dem Formular merken. Das kommt auf den konkreten Fall an.
...

Ja da hast du recht - Klassenvariablen sind fehl am Platz (hab grad rausgefunden, dass man da ganz schön perverse Sachen machen kann)
Ich nutz jetzt DeddyH´s Cracker-Klasse.

Sir Rufo 2. Jan 2013 15:48

AW: class helper
 
Ein
Delphi-Quellcode:
class helper
oder eine CrackerKlasse haben beide allerdings Nachteile: Die Wirkung ist nicht mehr gesteuert sondern gilt für alle betroffenen Klassen (in diesem Bereich).

Nehmen wir mal als Beispiel, dass du in einer TListBox eine Datei anzeigen möchtest und eben den Dateinamen dir merken möchtest, dann würde ich folgenden Ansatz bevorzugen:
Delphi-Quellcode:
unit TextFilePresenter;

interface

uses
  StdCtrls;

type
  TTextFilePresenter = class
  private
    FFileName : string;
    procedure SetFileName(const Value : string);
  protected
    procedure Present; virtual; abstract;
  public
    property FileName : string read FFileName write SetFileName;
  end;

  TTextFilePresenterListBox = class( TTextFilePresenter )
  private
    FListBox : TListBox;
    procedure SetListBox( const Value : TListBox );
  protected
    procedure Present; override;
  public
    property ListBox : TListBox read FListBox write SetListBox;
  end;

implementation

uses
  SysUtils;

procedure TTextFilePresenter.SetFileName( const Value : string );
begin
  FFileName := Value;
  Present;
end;

procedure TTextFilePresenterListBox.Present;
begin
  if Assigned( FListBox ) then
    if FileExists( FileName ) then
      FListBox.Items.LoadFromFile( FileName )
    else
      FListBox.Clear;
end;

procedure TTextFilePresenterListBox.SetListBox( const Value : TListBox );
begin
  FListBox := Value;
  Present;
end;

end.
Nun kann man sehr genau steuern, welche ListBox man damit ansprechen möchte.
Egal in welchem Formular und ohne Nebenwirkungen.

JamesTKirk 2. Jan 2013 16:30

AW: class helper
 
Zitat:

Zitat von stahli (Beitrag 1197351)
Die Funktion GetResult kann aber nur auf öffentliche Eigenschaften der Klasse zugreifen.

Das ist nicht ganz korrekt. Class helper können auf
Delphi-Quellcode:
published
,
Delphi-Quellcode:
public
,
Delphi-Quellcode:
protected
und
Delphi-Quellcode:
strict protected
Member der erweiterten Klasse zugreifen. Hatte ich nicht erwartet, kam aber raus, als ich Tests zur Implementierung der class helper in Free Pascal schrieb...

Dass class helper jedoch auch
Delphi-Quellcode:
class var
unterstützen war mir entfallen :shock:

Gruß,
Sven

Mschmidt 3. Jan 2013 16:08

AW: class helper
 
noch ein Nachtrag; class var geht, weil es keine Instanz-Variable ist. Es wäre genau so, als wenn du eine globale Variable deklarierst.
Class Var ist sogar verfügbar, wenn es keine Instanz gibt. Lediglich der Zugriff erfolgt via <Classname>.Variable statt nur Variable.
cheers Ms.

stahli 3. Jan 2013 16:31

AW: class helper
 
Zitat:

Zitat von Helmi (Beitrag 1197353)
Zitat:

Zitat von DeddyH (Beitrag 1197352)
In dem Fall muss man sich mit einer Cracker-Klasse behelfen, da geht das dann wieder.

Daran hab ich auch schon gedacht! - Dann ergibt sich für mich nicht wirklich der sinn von class helper
aber das ist eine andere Diskussion...

Mit einem Helper bleibt die Objekt-Klasse unangetastet.

Mit einer CrackerKlasse muss man das Objekt ggf. casten je nachdem von welcher Klasse das Objekt instanziiert wurde und wie es weiter verarbeitet werden soll. Streng genommen sind es nicht mehr die gleichen Klassen (auch wenn deren Namen noch gleich sind).

Mschmidt 3. Jan 2013 16:56

AW: class helper
 
Obwohl es vielleicht eher akademischer Natur ist, gefallen mir die Ansaetze hier eher nicht. Wozu gibt es die Vererbung?
Also

TmyListbox = class(tlistbox)
// erweiterungen einbauen

Ggf. Als neue Komponente installieren und verwenden.
Cheers mschmidt

Sir Rufo 3. Jan 2013 17:39

AW: class helper
 
Zitat:

Zitat von Mschmidt (Beitrag 1197514)
Obwohl es vielleicht eher akademischer Natur ist, gefallen mir die Ansaetze hier eher nicht. Wozu gibt es die Vererbung?
Also

TmyListbox = class(tlistbox)
// erweiterungen einbauen

Ggf. Als neue Komponente installieren und verwenden.
Cheers mschmidt

Das ist in diesem Zusammenhang eher kontraproduktiv.
Meine "Presenter" oder "Adapter" Klasse kann leicht auch auf andere visuelle Komponenten/Frameworks angepasst werden und über eine Factory kann ich diese verbinden. Dadurch kann ich der Factory z.B. ein TMemo, TListBox oder TComboBox (VCL sowie FMX) übergeben und bekomme eine Instanz vom Typ der Basisklasse zurück.

Bei dieser Instanz ändere ich die Eigenschaft FileName und fertig ist.

Mit deinem Ansatz müsste ich immer die konkrete Ableitung der Komponente ansprechen (ja, auch möglich per RTTI, aber schick ist das nicht).

Mschmidt 3. Jan 2013 18:46

AW: class helper
 
Es wird doch akademisch :-D, ok. In diesem Falle gebe ich dir recht. Jedoch faellt doch das argument, wenn man mehrere funktionalitaeten erweitern moechte. Mehrfachvererbung geht bei D nicht. Imho fange ich dann an, viele adapter zu bauen. Meine persoenliche Meinung ist, strikte Kapselung, sprich; nur die Klasse (visuell oder nicht) selbst kennt seine Funktionalitaeten, fremde Zugriffe erfolgen auf Properties/fkt/proc. Die implementation ist verborgen und physisch auf eine unit beschraenkt. Wird eine neue Funktionalitaet benoetigt, wird die klasse erweitert oder eine neue abgeleitet, nichts anderes. Leider ja, das ist ein
Hoher Aufwand, der sich aber in der Wartung bezahlt macht.
Cheers Mschmidt

Stevie 8. Jan 2013 11:42

AW: class helper
 
Mit Hilfe von DSharp kann man von TComponent abgeleitete Klassen sehr einfach um Properties erweitern:

Delphi-Quellcode:
unit ListBoxExtension;

interface

uses
  DSharp.Core.DependencyProperty,
  StdCtrls;

type
  TListBoxHelper = class helper for TListBox
  private
    class var FTestProperty: TDependencyProperty;
    class function GetTestProperty: TDependencyProperty; static;

    function GetTest: string;
    procedure SetTest(const Value: string);
  protected
    class property TestProperty: TDependencyProperty read GetTestProperty;
  public
    property Test: string read GetTest write SetTest;
  end;

implementation

{ TListBoxHelper }

function TListBoxHelper.GetTest: string;
begin
  Result := TestProperty.GetValue(Self).AsString;
end;

class function TListBoxHelper.GetTestProperty: TDependencyProperty;
begin
  if not Assigned(FTestProperty) then
    FTestProperty := TDependencyProperty.RegisterAttached('Test', TypeInfo(string), TListBox);
  Result := FTestProperty;
end;

procedure TListBoxHelper.SetTest(const Value: string);
begin
  TestProperty.SetValue(Self, Value);
end;

end.
Dahinter steckt nicht viel mehr als ein Dictionary, in dem die Werte für die Instanzen gespeichert werden, inklusive FreeNotification, um die Werte für freigegebene Instanzen zu löschen.

Uwe Raabe 8. Jan 2013 12:25

AW: class helper
 
Zitat:

Zitat von Stevie (Beitrag 1198206)
inklusive FreeNotification

Deswegen wohl auch die Einschränkung auf TComponent-Derivate. Trotzdem, coole Sache :thumb:

Stevie 8. Jan 2013 12:27

AW: class helper
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1198214)
Deswegen wohl auch die Einschränkung auf TComponent-Derivate.

Genau, bei anderen Klassen kann man nicht mitbekommen, wenn eine Instanz freigegeben wird (zumindest nicht, ohne sich in das FreeInstance zu hacken :mrgreen:).

Der schöne Günther 2. Aug 2013 13:20

AW: class helper
 
Hinweis: Der letzte Beitrag in diesem Thema ist älter als ein halbes Jahr.

Ich erlaube mir, trotzdem zu schreiben. Falls das nicht ok bist, einfach löschen :-)


Ich sehe die Problematik, dass Instanzvariablen "angeblich" nicht erlaubt sind, nicht: Was hält mich denn davon ab, eine
Delphi-Quellcode:
TDictionary
-Klassenvariable zu deklarieren, die als Schlüssel eben die Instanz selbst hält?

Konkretes Beispiel: Ein TButtonHelper soll mir ermöglichen, dass jeder Button weiß, wann er das letzte mal geklickt worden ist:

Delphi-Quellcode:
  TButtonHelper = class helper for TButton
   private
      class var lastPressedDictionary: TDictionary<TButton, TDateTime>;
[...]
      property lastClicked: TDateTime
         read GetlastClicked
         write SetlastClicked;

  end;
In meiner Anwendung kann ich nun problemlos sagen
Delphi-Quellcode:
Button99.lastClicked := Now(); Button123.lastClicked := Yesterday();
. Natürlich ist das etwas aufwändiger als die Möglichkeit, einfache Instanzvariablen in einer Helper-Klasse zu haben, aber das beste, was ich kenne.

In der Praxis habe ich bei VCL-Komponenten natürlich noch eine Art "enable/disableHelperFunctionality()" da man das TDictionary ja auch wieder abbauen sollte...

Uwe Raabe 2. Aug 2013 14:51

AW: class helper
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1223298)
Was hält mich denn davon ab, eine
Delphi-Quellcode:
TDictionary
-Klassenvariable zu deklarieren, die als Schlüssel eben die Instanz selbst hält?

Gar nichts - solange du deinen Instanzen-Haushalt in Ordnung hältst und die Dictionary-Einträge löscht, wenn die Instanz freigegeben wird.

JamesTKirk 9. Aug 2013 12:18

AW: class helper
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1223298)
Was hält mich denn davon ab, eine
Delphi-Quellcode:
TDictionary
-Klassenvariable zu deklarieren, die als Schlüssel eben die Instanz selbst hält?

Genau das (nur ein bisschen versteckt) scheint ja dieses
Delphi-Quellcode:
TDependencyProperty
aus DSharp zu machen.

Gruß,
Sven

Der schöne Günther 9. Aug 2013 20:14

AW: class helper
 
Elaborieren Sie.

Uwe Raabe 9. Aug 2013 21:00

AW: class helper
 
Zitat:

Zitat von Stevie (Beitrag 1198206)
Mit Hilfe von DSharp kann man von TComponent abgeleitete Klassen sehr einfach um Properties erweitern:
...
Dahinter steckt nicht viel mehr als ein Dictionary, in dem die Werte für die Instanzen gespeichert werden, inklusive FreeNotification, um die Werte für freigegebene Instanzen zu löschen.

Mit der FreeNotification von TComponent kann man die zusätzlichen Instanzen freigeben, wenn die zugehörige Original-Instanz freigegeben wird. Bei nicht von TComponent abgeleiteten Klassen fehlt dieser Mechanismust und man muss sich etwas anderes überlegen.

JamesTKirk 10. Aug 2013 10:43

AW: class helper
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1224187)
Elaborieren Sie.

Lies dir doch nochmal den Beitrag von Stevie mit dem Quellcode durch. Ich zitier ihn dir mal:

Zitat:

Zitat von Stevie (Beitrag 1198206)
Mit Hilfe von DSharp kann man von TComponent abgeleitete Klassen sehr einfach um Properties erweitern:

{Quellcode mit TDependencyProperty}

Dahinter steckt nicht viel mehr als ein Dictionary, in dem die Werte für die Instanzen gespeichert werden, inklusive FreeNotification, um die Werte für freigegebene Instanzen zu löschen.

Das ist quasi das was du vorgeschlagen hast nur halt schön verpackt, aber mit der Einschränkung, dass es nur für Klassen geeignet ist, die von
Delphi-Quellcode:
TComponent
ableiten (wegen
Delphi-Quellcode:
FreeNotification
).

Gruß,
Sven


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