Delphi-PRAXiS
Seite 2 von 2     12   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Klasseninstanz zur Laufzeit bestimmen (https://www.delphipraxis.net/186082-klasseninstanz-zur-laufzeit-bestimmen.html)

Delbor 2. Aug 2015 19:14

AW: Klasseninstanz zur Laufzeit bestimmen
 
Hi zusammen

Sorry, wenn ich auf Sir Rufos Vorschlag bisher nicht eingegangen bin; der Grund liegt vor allem darin, dass ich bisher sehr wenig mit Generics arbeite, da ich die Dinger zu wenig durchschaue.
Eigentlich ist die einzige generische Klasse, die ich bisher benutze, eine Objectliste, die mir einst DeddyH vorgeschlagen hat. Ein weiterer seiner Vorschläge betraf die Verwendung einer Class Factory.Und so sieht das Ding heute bei mir aus :

Delphi-Quellcode:
uses Generics.Collections, Vcl.Forms, System.Classes, System.SysUtils;

type
  EFrameNotRegistered = class(Exception);
  TFrameClass = class of TFrame;

  TFrameFactory = class abstract
  strict private
    class var FAssociations: TDictionary<string, TFrameClass>;
    class constructor Create;
    class destructor Destroy;
  public
    class procedure RegisterFrameClass(const Extension: string; FrameClass: TFrameClass);
    class function GetRegisteredFrameClass(const Extension: string; AOwner: TComponent): TFrame;
  end;

implementation

{ TFrameFactory }

class constructor TFrameFactory.Create;
begin
  FAssociations := TDictionary<string, TFrameClass>.Create;
end;

class destructor TFrameFactory.Destroy;
begin
  FAssociations.Free;
end;

class function TFrameFactory.GetRegisteredFrameClass(const Extension: string;
  AOwner: TComponent): TFrame;
  var AFrameClass: TFrameClass;  //TCustomFrameClass
begin
  if FAssociations.TryGetValue(AnsiLowerCase(Extension), AFrameClass) then
    Result := AFrameClass.Create(AOwner)
  else
    raise EFrameNotRegistered.CreateFmt('Für die Endung %s ist keine Frameklasse registriert.', [Extension]);
end;

class procedure TFrameFactory.RegisterFrameClass(const Extension: string;
  FrameClass: TFrameClass);
begin
  FAssociations.AddOrSetValue('.' + AnsiLowerCase(Extension), FrameClass);
end;

end.
Meinen Frames muss ich dann noch folgendes verpassen:
Delphi-Quellcode:
initialization
  TFrameFactory.RegisterFrameClass('css', TCSSFrame);
Dein Vorschlag ist offenbar das generische Gegenstück zu dieser Fabrikklasse, und wenn mich meine bisherigen Kenntnisse der Generics nicht täuschen, könnte ich da jede Klasse registrieren, die, in deinem Beispiel, von TFoo, bzw. TBar erbt oder selbst von einem dieser Typen ist.
Somit könnte ich da jeden meiner Eventhandler registrieren. Hab ich das richtig verstanden?

Gruss
Delbor

Sir Rufo 2. Aug 2015 19:25

AW: Klasseninstanz zur Laufzeit bestimmen
 
Genau so isses :)

Delbor 3. Aug 2015 19:24

AW: Klasseninstanz zur Laufzeit bestimmen
 
Hi zusammen

Wie man nur so blind sein kann!! Nochmal zur Erinnerung die CreateAufrufe und die Createprozedur selbst:
Delphi-Quellcode:
constructor TJavaScriptAttriTLBXFrame.Create(AOwner: TComponent);
begin
  inherited;
  FJavaScriptAttributsList := TDataObjectList<TAttributsClass>.Create();
  FJavaScriptAttributsList.OwnsObjects := True;

  CreateAttribute(FCommentAttri, 'Kommentare', 'FCommentAttri');           //    fCommentAttri: TSynHighlighterAttributes;
  CreateAttribute(FIdentifierAttri, 'Bezeichner', 'FIdentifierAttri');       //    fPropertyAttri: TSynHighlighterAttributes;
  CreateAttribute(FKeyAttri, 'Schlüsselworte', 'FKeyAttri');          //    fKeyAttri: TSynHighlighterAttributes;

  CreateAttribute(FNonReservedKeyAttri, 'Nicht reservierte Schlüssel', 'FNonReservedKeyAttri');           //    fSpaceAttri: TSynHighlighterAttributes;
  CreateAttribute(FEventAttri, 'Ereignisse', 'FEventAttri');              //    fStringAttri: TSynHighlighterAttributes;
  CreateAttribute(FNumberAttri, 'Zahlen', 'FNumberAttri');              //    fNumberAttri: TSynHighlighterAttributes;


  CreateAttribute(FSpaceAttri, 'Leerzeichen', 'FSpaceAttri');                //    fColorAttri: TSynHighlighterAttributes;
  CreateAttribute(FStringAttri, 'Strings', 'FStringAttri');                 //    fTextAttri: TSynHighlighterAttributes;
  CreateAttribute(FSymbolAttri, 'Symbole', 'FSymbolAttri');             //    fSymbolAttri: TSynHighlighterAttributes;
end;

destructor TJavaScriptAttriTLBXFrame.Destroy;
begin
   FJavaScriptAttributsList.Free;
  inherited;
end;

procedure TJavaScriptAttriTLBXFrame.CreateAttribute(var AAttrib: TAttributsClass; const AName: string; AInstanz : String);
begin
  AAttrib:= TAttributsClass.Create(Self);
  AAttrib.AttributName := AName;
  AAttrib.InstanzName := AInstanz;
  FJavaScriptAttributsList.Add(AAttrib);
  CmbxAttributes.Items.AddObject(AName,TObject(AAttrib));
  Application.ProcessMessages;
end;
Damit wird bei jedem Aufruf eine Instanz der Klasse TAttributsClass erzeugt und nicht eine Klasseninstanz FCommentAttri - somit kann ich auch nicht auf eine Instanz FCommentAttri zugreifen. Einfach, nicht?

Um die richtigen Instanzen zu erzeugen, müsste ich wohl eher so etwas macen:
Delphi-Quellcode:
procedure TJavaScriptAttriTLBXFrame.CreateAttribute(var AAttrib: TAttributsClass; const AName: string; AInstanz : String);
begin
  AAttrib:= AAttrib.Create(Self);
  AAttrib.AttributName := AName;
  AAttrib.InstanzName := AInstanz;
  FJavaScriptAttributsList.Add(AAttrib);
  CmbxAttributes.Items.AddObject(AName,TObject(AAttrib));
  Application.ProcessMessages;
end;
Wobei dies auch nicht ganz stimmt: Der rechte Teil muss ein Klassenbezeichner sein - der muss aber zum Instanzbezeichner passen. Das property Instanzname ist auch nicht das gelbe vom Ei...
Das heisst nichts anderes, als dass es einige von TAttributsClass abgeleitete Klassen geben wird, wie zum Beispiel TCommentAttri. Die Erzeugung ist dann:
Delphi-Quellcode:
FCommentAttri:= TCommentAttri.Create
Damit habe ich ohne Verrenkungen zugriff auf die richtige Instanz.

Gruss
Delbor

Delbor 4. Aug 2015 09:45

AW: Klasseninstanz zur Laufzeit bestimmen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hi zusammen

Die Idee aus meinem vorigen Beitrag war wirklich nicht schlecht - sie liefert eine Antwort auf die Frage, die der Threadtitel vorgibt - aber sie ist noch weniger das Gelbe vom Ei, wie das Property 'InstanzName' der Klasse TAttributsClass, da sie zu einem, wie ich meine, ziemlich seltsamen Konstrukt führt:
Delphi-Quellcode:
  TAttributsClass = Class(TPersistent)
     public
      BackGround : TColor;
      ForeGround : TColor;
      StyleBold: Boolean;
      StyleItalic : Boolean;
      StyleUnderLine : Boolean;
      StyleStrikeOff : Boolean;
      AttributName: String;
      InstanzName: String;
      Constructor Create(AOwner: TComponent);
      Destructor Destroy; override;
  end;

  TJavaScriptCommentClass = Class(TAttributsClass);
  TJavaScriptIdentifierClass = Class(TAttributsClass);
  TJavaScriptKeyClass = Class(TAttributsClass);

  TJavaScriptNonReservedKeyClass = Class(TAttributsClass);
  TJavaScriptEventClass = Class(TAttributsClass);
  TJavaScriptNumberClass = Class(TAttributsClass);
//
  TJavaScriptSpaceClass = Class(TAttributsClass);
  TJavaScriptStringClass = Class(TAttributsClass);
  TJavaScriptSymbolClass = Class(TAttributsClass);
Dabei führen die von TAttributsClass abgeleiteten Klassen keinerlei neue Member ein, weder Eigenschaften Methoden oder Ereignisse - wobei ich mir schon überlegt habe, ob die Dinger nicht selbst ein Event abfeuern könnten...
Aber ich habe etwas viel besseres gefunden..

Ein Test in einem meiner Synedit-Frames brachte das gewünschte Resultat, ersichtlich im angehängten Jpeg:

Delphi-Quellcode:
procedure TCSSFrame.SynEdit1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  LContext: TRttiContext; LType: TRttiType; LProperty: TRttiProperty;
  LMethod: TRttiMethod; LField: TRttiField;
begin
  if Self.SynEdit1.SelText <> '' then begin
    Label1.Caption := SynEdit1.SelText;
      Label1.Caption := Label1.Caption + ' SynEdit1.TabWidth := '+ IntToStr(SynEdit1.TabWidth);
    if assigned(FOnSelectText) then    // Dieser Event wird gefeuert,
      FOnSelectText(Self);             // wenn Text markiert wird.
  end;
  LContext := TRttiContext.Create;
    try
      LType := LContext.GetType(TSynCssSyn);
      SynEdit1.lines.Add(LType.ToString);
      for LProperty in LType.GetProperties do
      begin
        SynEdit1.lines.Add (LProperty.ToString);
      end;
    finally
    LContext.Free;
  end;
end;
Wie der Anhang zeigt, liefert mir das alle Propertys als String. Um meine neuen Einstellungen aus meinem Frame richtig zuordnen zu können, brauche ich entweder einen Kalssenbezeichner, der dem Probertynamen entspricht oder einen String, den ich mitt dem als String vorliegenden Propertynamen vergleichen kann.
Natürlich muss ich aus der sich ergebenden Liste noch den (Teil-)String filtern, den ich in TAttributsClass.Instanzname übergebe. Aber das dürfte wohl kaum ein Problem sein.
Ein weiterer Effekt: Ich brauche zur Übergabe noch genau einen Eventtyp.

Oder gibts noch eine bessere Lösung?

Gruss
Delbor


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:22 Uhr.
Seite 2 von 2     12   

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