*Thread ausgrab*
Ich bin mit dem Pattern immer noch nicht wirklich zufrieden. Im Prinzip hätte ich gerne einen Observer, der für alle Events herhalten kann. Ich habe jetzt dazu mit der
RTTI ein wenig gebastelt.
Der Ansatz sieht so aus:
Delphi-Quellcode:
unit thObserver;
interface
uses System.Generics.Collections,
Rtti;
type
TObseverItem =
class
public
Methods: TList<TRttiMethod>;
constructor Create;
destructor Destroy;
override;
end;
TObserver =
class(TDictionary<
string, TObseverItem>)
public
procedure RegisterObserver(EventName:
string; Method: TRttiMethod);
procedure UnregisterObserver(EventName:
string; Method: TRttiMethod);
procedure Call(EventName:
string; Args:
array of TValue);
end;
implementation
procedure TObserver.Call(EventName:
string; Args:
array of TValue);
var
Item: TRttiMethod;
begin
for Item
in Items[EventName].Methods
do
begin
Item.Invoke(Item, Args)
end;
end;
procedure TObserver.RegisterObserver(EventName:
string;
Method: TRttiMethod);
begin
if not ContainsKey(EventName)
then
begin
Add(EventName, TObseverItem.Create);
end;
Items[EventName].Methods.Add(Method);
end;
procedure TObserver.UnregisterObserver(EventName:
string;
Method: TRttiMethod);
begin
Items[EventName].Methods.Remove(Method);
if Items[EventName].Methods.Count = 0
then
begin
Items[EventName].Free;
Remove(EventName);
end;
end;
{ TObseverItem }
constructor TObseverItem.Create;
begin
Methods := TList<TRttiMethod>.Create;
end;
destructor TObseverItem.Destroy;
begin
Methods.Free;
inherited;
end;
end.
Es läuft aber noch nicht rund. Im Moment sieht das ganze im Aufruf so aus:
Delphi-Quellcode:
type
TMyClass = class
private
FObserver: TObserver;
public
constructor Create;
destructor Destroy; override;
procedure RegisterOnChange(Method: TRttiMethod);
procedure UnregisterOnChange(Method: TRttiMethod);
end;
{...}
// zum auslösen, um z.B. Button1 als Parameter zu übergeben (wir gehen davon aus, dass die registrierte Funktion auch darauf passt
FObserver.Call('OnChange', [TValue.From<TButton>(Button1)]);
Um jetzt von außerhalb mich an das Event anzuhängen, mache ich folgendes:
Delphi-Quellcode:
procedure TForm1.TestOnChange(Button: TButton);
begin
// ...
end;
{...}
var
Method: TRttiMethod;
context: TRttiContext;
begin
Method := context.GetType(Self.ClassType).GetMethod('TestOnChange');
Myclass.RegisterOnAquireAchievement(Method);
Das ganze geht so lange gut, bis der Aufruf über das Invoke erfolgt, dann hagelt es eine AccessViolation. Ist das so, wie ich das vorhabe überhaupt möglich? (Noch schöner wäre, wenn man sich mit Include und Exclude an das Event anhängen könnte, ähnlich wie in .NET. Also dann sowas wie
Myclass.OnChange.Include(MeinEventHandler)
, aber da weiß ich auch nicht, ob und wie das geht).