Rzutowanie metod

Deti

Nie raz pewnie zdarzyło się, że masz pełno procedur / funkcji w swoich programach i trzeba je uruchamiać w zależności od wywołania. Pokaże tu jak odpalić daną metodę w bardziej dynamiczny sposób.

Najpierw aby zrozumieć co tu robimy - stwórz kilka procedur w swoim programie np.:

procedure MyProc1;
procedure MyProc2;
procedure MyProc3;

Dodaj te zapisy do części interface, a następnie dodaj jakieś przykładowe instrukcje do implementacji. Ale uwaga!: procedury muszą być zadeklarowane w sekcji "published" aby można było na nie rzutować. Gdy już to zrobisz.. - wstaw na przykład na formę button oraz kontrolkę TEdit, niech się nazywają Button1 oraz odpowiednio Edit1. I jeszcze jedna rzecz: wszystkie te 3 procedury są jednego typu ... - dodaj też nowy typ, który będzie odpowiadał tej właśnie budowie metody:

type
  TProcedureType = Procedure of Object;

Zrobimy teraz tak, aby po kliknięciu na Button1 wywołała się ta procedura, której nazwa widnieje w naszej kontrolce. Obsługujemy zatem standardowo zdarzenie Button1.OnClick:

procedure TForm1.Button1Click(Sender: TObject);
var
  ProcRelay : TProcedureType;
begin
  @ProcRelay:= MethodAddress(Edit1.Text);
   if @ProcRelay= nil then
    ShowMessage('Nie ma takiej metody')
  else
  ProcRelay;
 end;

Jak widać nie jest to specjalnie trudne - najpierw tworzymy sobie zmienną, która będzie wskazywac na naszą metodę. Później przypisujemy jej adres tej metody, którą sobie ktoś wybrał w kontrolce Edit1. Później sprawdzamy, czy w ogóle taka metoda istnieje - jeśli nie to zostaniemy o tym odpowiednio poinformowani :) Jeśli natomiast wszystko pójdzie zgodnie z oczekiwaniami - odpalamy naszą procedurę.

Jak widać sposób ten zaoszczędzi nam nieco zbędnego kodu i wprowadzi trochę zaawansowanego dynamizmu do programu.. :)

Można też zrobić to jako odzielna funkcja, której na wejściu bedzie podawana metoda, a zwracać będzie wartość wartość typu Boolean (True - metoda istnieje, False - metoda nie istnieje) ... przy pierwszej możliwości funkcja ta może oczywiscie daną metodę odpalać.

Również zamiast "Procedure of Object" można dać inny dowolny typ procedury, funkcji...

8 komentarzy

Bardzo dobre! Rzeczywiście wiele kodu i CZASU takie coś pozwala zaoszczędzić... Trafny artykuł!

niezłe, choć osobiście szukam czegoś takiego na typy messejdżów ;] - np. WM_QUIT, WM_SHOW itd. jest jakaś podobna rzecz?

Hmm, może sprawdź, jak VCL to robi, to może się uda?? :) ASM i cierpliwość :)

szkoda troszkę, że poszedłeś na łatwizne :( trzeba było opisać to samo tylko z wykorzystaniem funkcji z parametrem, gdyż częściej występują w programach niż proceduty bez para,etru...

ps. najlepiej jakbyś poprawil

No fajne... ale aby nie pisac za duzo, to wszedzie tam gdzie chce sie wykorzystac wskazniki na funkcje, stosuje sie "funktory"... ot, taki idiom w programowaniu obiektowym. Pozdrawiam.

Witam.
Jak ProcRelay : TProcedureType; przechować w var Event: TNotifyEvent;?

Witam.
Dla Milka:
Oto rzutowanie funkcji z parametrem:

Założenie: procedura (lub funkcja) np taka:

procedure proc1(i:integer;x:string);

Type TProc=procedure(i:integer;x:string) of object;
i potem

var met:Tmethod;
  p:Pointer;
  pr:Tproc;
begin
 p:=Tform.MethodAddress(\'proc1\');
 met.code:=p;
 p:=form1;
 met.data:=p;//to nie jest konieczne ponieważ nasza metoda nie 
                    //wykożystuje parametru self w swojej definicji, ale dajmy
 pr:=TProc(met);
 pr(1,\'ala\');
end;

Pragnę dodać, iż mimo iż w danej chwili jakiś objekt może mieć dostęp do dwóch metod (np z parametrem lub bez), to MethodAdress jest metodą klasy. Oznacza to, iż pobiera adres metody z typu a nie z objektu(choć z objektu też zadziała). A Dana klasa nie możę mieć dwóch opublikowanych metod o takiej samej nazwie(przeciążonych). Mówiąc inaczej jak dla objektu mamy możliwość przez np form1.procedura wybrać dwie procedury do dostęp i tak mamy tylko do jednej(do tej zadeklarowanej w Typie takim jaki jest form1). Żeby wam jeszcze troche namieszać w głowie to powiem że to idealna metoda aby obejść metody statyczne. Przykład:
Klasa1 ma procedure do;
Klasa2(dziedzicząca od klasa1) definiuje nową procedure do. Obie są statyczne. Teraz mamy objekt: var ob:Klasa1; i potem ob:=TKlasa2.create to wywołując ob.do wywołamy procke z klasa1(bo nie są virtualne). MethodAddress (ale nie z klasy tylko już z ob) mamy dostęp do metody Klasa2.do
Pozdrawiam:)

No fajne... ale aby nie pisac za duzo, to wszedzie tam gdzie chce sie wykorzystac wskazniki na funkcje, stosuje sie "funktory"... ot, taki idiom w programowaniu obiektowym. Pozdrawiam.