Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Anonyme Methoden in Attributen (https://www.delphipraxis.net/183880-anonyme-methoden-attributen.html)

Neutral General 11. Feb 2015 14:05

Delphi-Version: XE7

Anonyme Methoden in Attributen
 
Hallo,

Das scheint leider nicht zu gehen:
Delphi-Quellcode:
TAnonym = reference to procedure;

TMyCustomAttribute = class(TCustomAttribute)
  constructor Create(Anonym: TAnonym); // compiliert
end;

// Funktioniert nicht: "Konstantenausdruck erwartet"
[TMyCustomAttribute(
  procedure
  begin
    ShowMessage('Test');
  end)
]
TTestclass = class

end;
Das finde ich schade und ich wäre froh wenn Embarcadero sowas implementieren könnte.
Denn ich finde dass der Ausdruck konstant genug ist.

Ansonsten: Kennt jemand eine Alternative?

himitsu 11. Feb 2015 14:41

AW: Anonyme Methoden in Attributen
 
Bei anonymen Methoden wird zur "Laufzeit" ein Interface erstellt und du kannst dort nur Konstanten verwenden.

Stevie 11. Feb 2015 14:58

AW: Anonyme Methoden in Attributen
 
Die Alternative heißt: neue Attribute Klasse, und dort den Code implementieren.

sahimba 11. Feb 2015 15:34

AW: Anonyme Methoden in Attributen
 
Spannend. Hab gar nicht geahnt, dass so etwas geht. Wann genau wird der Code dann ausgeführt? Einmalig, zur Initialisierung oder immer, wenn auf das Attribut zugegriffen wird? Spontan fällt mir kein Use Case ein, aber das heisst ja nix :lol:

Stevie 11. Feb 2015 15:47

AW: Anonyme Methoden in Attributen
 
Zitat:

Zitat von sahimba (Beitrag 1289529)
Spannend. Hab gar nicht geahnt, dass so etwas geht.

Naja, es geht ja ebend nicht ;)
Zitat:

Zitat von sahimba (Beitrag 1289529)
Wann genau wird der Code dann ausgeführt? Einmalig, zur Initialisierung oder immer, wenn auf das Attribut zugegriffen wird?

Der Konstruktor eines Attributes wird beim Auslesen ausgeführt. Sofern man dafür sorgt, dass die Rtti Objekte am Leben bleiben (indem man sich den TRttiContext behält), ist das einmalig.

In dieser Hinsicht ist Delphi übrigens mal ausnahmsweise C# nicht hinterher, die können auch nicht mehr.

sahimba 11. Feb 2015 15:56

AW: Anonyme Methoden in Attributen
 
Zitat:

Zitat von Stevie (Beitrag 1289530)
Naja, es geht ja ebend nicht ;)

Aber fast. Quasi. Quasi fast 8-)

Neutral General 11. Feb 2015 16:02

AW: Anonyme Methoden in Attributen
 
Zitat:

Zitat von Stevie (Beitrag 1289530)
Zitat:

Zitat von sahimba (Beitrag 1289529)
Spannend. Hab gar nicht geahnt, dass so etwas geht.

Naja, es geht ja ebend nicht ;)
Zitat:

Zitat von sahimba (Beitrag 1289529)
Wann genau wird der Code dann ausgeführt? Einmalig, zur Initialisierung oder immer, wenn auf das Attribut zugegriffen wird?

Der Konstruktor eines Attributes wird beim Auslesen ausgeführt. Sofern man dafür sorgt, dass die Rtti Objekte am Leben bleiben (indem man sich den TRttiContext behält), ist das einmalig.

In dieser Hinsicht ist Delphi übrigens mal ausnahmsweise C# nicht hinterher, die können auch nicht mehr.

Der Code würde gar nicht automatisch ausgeführt werden. (Es sei denn der constructor ruft ihn sofort auf) In meinem Fall hat eine Klasse an anderer Stelle die Attribute ausgelesen und sollte dann die dem Attribut mitgegebene anonyme Funktion ausführen.Habe jetzt eine andere Möglichkeit gefunden für mein Problem - auch wenn es nicht gerade das Schönste ist was ich je getan habe.

Daniel 11. Feb 2015 16:08

AW: Anonyme Methoden in Attributen
 
Zitat:

Zitat von Neutral General (Beitrag 1289532)
Auch wenn es nicht gerade das Schönste ist was ich je getan habe.

... zeigen! :mrgreen:

himitsu 11. Feb 2015 16:26

AW: Anonyme Methoden in Attributen
 
Zitat:

Zitat von sahimba (Beitrag 1289529)
Spannend. Hab gar nicht geahnt, dass so etwas geht. Wann genau wird der Code dann ausgeführt? Einmalig, zur Initialisierung oder immer, wenn auf das Attribut zugegriffen wird? Spontan fällt mir kein Use Case ein, aber das heisst ja nix :lol:

Die werden nicht beim Start ausgeführt, sondern nur wenn "jemand" sie ausliest.
http://docwiki.embarcadero.com/RADSt...it_extrahieren
http://docwiki.embarcadero.com/RADSt...te_deklarieren

Es gibt von Delphi ein paar Attribute, welche von Compiler oder VCL ausgewertet werden, wie z.B.
Delphi-Quellcode:
property int: Integer default 123;

[Default('abc')]
property str: string;

var
  [Weak] i: IInterface;
WeakAttribute, UnsafeAttribute, RefAttribute, DefaultAttribute, NoDefaultAttribute, StoredAttribute, ObservableMemberAttribute, ...

Neutral General 11. Feb 2015 17:04

AW: Anonyme Methoden in Attributen
 
Zitat:

Zitat von Daniel (Beitrag 1289534)
Zitat:

Zitat von Neutral General (Beitrag 1289532)
Auch wenn es nicht gerade das Schönste ist was ich je getan habe.

... zeigen! :mrgreen:

Das ist gar nicht so einfach und ohne ganz großen Kontext sieht es wahrscheinlich noch schlimmer aus :X

Ich versuche es mal zu erklären. Grob gesagt:
Es gibt eine abstrakte Basisklasse "TSearchProvider" mit einer Methode Search() und einer Liste von Methodenzeigern (TProcessSearchResult= procedure(SearchResult: TSearchResultObject) of object) die Funktionen auf die Daten bereitstellen die der SearchProvider findet.

Nun gibt es ein "TSearchEngine" (Singleton) bei der TSearchProvider-Ableitungen (Instanzen dieser Ableitungen) registriert werden können.

Im Programm selbst wird nur SearchEngine.Search('Suchbegriff') aufgerufen was daraufhin intern das Search('Suchbegriff') aller registrierten TSearchProvider-Objekte aufruft.

TSearchEngine gruppiert die Suchergebnisse nach Providern. D.h. SearchEngine.Search gibt eine Liste mit Listen der Suchergebnisse der einzelnen Searchprovider zurück. Diese Listen (in der großen Liste) besitzen eine Referenz auf den Searchprovider der diese Liste "beigetragen" hat.

Derjenige der nun SearchEngine.Search aufruft hat nun quasi sowas wie eine TGroupedResultList<TSearchProviderResultList<TSear chResultObject>> mit den Ergebnissen (wie gesagt: pro Searchprovider eine Liste).

Der Aufrufer stellt nun die Ergebnisse in einem Grid dar und möchte dem Endbenutzer nun die Möglichkeit geben Operationen auf den Suchergebnissen auszuführen (per Popupmenu z.B.). Die SearchEngine hat natürlich keine Ahnung was für Daten sie da ausgespuckt hat.

ALLERDINGS gibt es da die Liste der Operationen die die einzelnen SearchProvider bereitstellen (können). TSearchProviderResultList beinhaltet eine Referenz auf den Provider der wiederrum die Liste mit Methoden inkl. Caption für die Methoden bereitstellt.

Nun kann der Aufrufer beim Popup des Popupmenus einfach dynamisch das Popupmenu mit den Aktionen füllen die im zum ausgewählten Suchergebnis zugehörigen Searchprovider enthalten sind.
Nun kann es aber sein dass nicht für alle Suchergebnisse des selben Providers alle Operationen zur Verfügung stehen sollen (z.B. ein SearchProvider für Adressen mit der Operation "Bestellung schreiben", die natürlich nur bei Lieferanten und nicht bei Kunden angezeigt werden soll).

Soweit so toll. Falls noch jemand am lesen ist: Nun zum eigentlichen Problem:

Ich dachte damit derjenige der von TSearchProvider ableitet es relativ bequem hat erlaube ich einfach sowas:

Delphi-Quellcode:
TAdresseSearchProvider = class(TSearchProvider)
public
  // Search() für Adressen überschreiben + anderer Kram ...

  // Operationen
  [TSearchResultOperation('Bestellung schreiben',
     function(AData: TSearchResultObject): Boolean
     begin
       Result := TAdressResult(AData).IstLieferant;
     end)
  ]
  procedure BestellungSchreiben(AData: TSearchResultObject);
end;
Im constructor des abstrakten TSearchproviders würde ich dann per RTTI die Methoden durchlaufen und nach Methoden mit dieser Signatur + dem TSearchResultOperation-Attribut suchen und diese automatisch zu der Operationen-Liste hinzufügen mit dem im Attibut angegebenen Namen und anonymen Check-Procedure.

Somit muss derjenige der Ableitet sich um das hinzufügen zur Operationen-Liste nicht kümmern.

Und beim Aufbau des PopupMenüs:
Delphi-Quellcode:
for i:= 0 to SelSearchResultObject.Provider.Operations.Count-1 do
begin
  // ACHTUNG: Starker Pseudocode:
  if SelSearchResultObject.Provider.Operations[i].CanUse(SelSearchResultObject) then // <-- anonyme Methode aus dem Attribut
    PopupMenu.AddItem(SelSearchResultObject.Provider.Operations[i].Name, SelSearchResultObject.Provider.Operations[i].Code);
end;
Da das mit den anonymen Methoden in den Attributen nicht geht habe ich es so gemacht dass TSearchProvider beim Durchsuchen der Methoden nachdem es eine Methode mit TSearchResultOperation-Attribut gefunden hat nach einer Methode sucht die genauso heißt + 'Check':

Delphi-Quellcode:
oeprationCheckMethod := rttiType.GetMethod(operationMethod.Name + 'Check');
Und diese wird dann in Operation.CanUse eingetragen.
Der SearchProvider sieht dann so aus:

Delphi-Quellcode:
TAdresseSearchProvider = class(TSearchProvider)
public
  // Search() für Adressen überschreiben + anderer Kram ...

  // Operationen
  [TSearchResultOperation('Bestellung schreiben']
  procedure BestellungSchreiben(AData: TSearchResultObject);
  function BestellungSchreibenCheck(AData: TSearchResultObject): Boolean;
end;
Und das ist nicht so schön.
Und Daniel: Ich hoffe du hast auch brav alles gelesen, du wolltest es ja wissen :mrgreen: :P

(Ich hoffe man konnte es halbwegs verstehen :oops: )

Stevie 11. Feb 2015 17:41

AW: Anonyme Methoden in Attributen
 
Mit nem Command Pattern hätte sich das sauberer lösen lassen:

Delphi-Quellcode:
type
  ISearchResultCommand = interface
    function CanExecute(AData: TSearchResultObject): Boolean;
    procedure Execute(AData: TSearchResultObject);
  end;
Das wird dann von allen deinen Operationen z.B.
Delphi-Quellcode:
TBestellungenSchreiben
implementiert.
Ganz nebenbei hast du deinen Code noch schön entkoppelt und deine ganzen Operationen sitzen nicht in den TSearchProvider Klassen sondern sind eigenständig.

Zudem ist das ganze viel flexibler denn nun kannst du bei den search providern eine Liste von ISearchResultCommand reinpacken, gibst den Dingern noch ne Name oder Caption Property und bumms ist das einbauen einer neuen Operation ein Kinderspiel: einfach neue Klasse bauen und zur Liste hinzufügen. Damit kannste mit einer Schleife bequem dein Kontextmenü befüllen.

Sir Rufo 11. Feb 2015 21:06

AW: Anonyme Methoden in Attributen
 
Hüstel ... das macht man doch generisch :stupid:
Delphi-Quellcode:
ICommand = interface
  function CanExecute : Boolean;
  procedure Execute;
end;

ICommand<T> = interface
  function CanExecute( const Value : T ) : Boolean;
  procedure Execute( const Value : T );
end;

Stevie 11. Feb 2015 22:05

AW: Anonyme Methoden in Attributen
 
Zitat:

Zitat von Sir Rufo (Beitrag 1289571)
Hüstel ... das macht man doch generisch :stupid:

Macht man nicht, weil man dann mit GUIDs verschissen hat... :roll:

himitsu 11. Feb 2015 22:20

AW: Anonyme Methoden in Attributen
 
Nja, man muß den Generic nur nochmal ableiten, dann geht das schon.
Und das Problem gibt es nur, wenn man auf das Interface prüfen/casten will/muss.

Delphi-Quellcode:
type
  // falsch
  IIntegerCommand = ICommand<Integer>;

  // richtig
  IIntegerCommand = Interface(ICommand<Integer>)
    ['{00000000-0000-0000-C000-000000000046}']
  end;
Genau so muß man es auch machen, wenn man generische Klassen in der VCL benutzen will, denn < und > ist in Typen dort nicht erlaubt. :roll:


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