![]() |
Generics: Instanz-Erzeugung in generischer Klasse
Ich habe hier eine generische Klassen/Interface Definition, die von TComponent abgeleitet ist.
Wie bekomme ich die Klasse jetzt dazu eine passende Instanz zu erzeugen?
Delphi-Quellcode:
sagt ja nur aus, dass die Klasse T einen parameterlosen Constructor hat.
TGenClass<T:Class, Constructor> = class
TComponent hat aber einen Constructor mit Parameter (obwohl ich dem ledigleich ein schnödes nil mitgeben würde). Das
Delphi-Quellcode:
findet der Compiler gut, aber es ist nicht so wie ich das möchte, denn
T.Create;
Delphi-Quellcode:
und
var
MyButton : IGenericLink<TButton>; begin MyButton := TGenericLink<TButton>.Create; MyButton.Link.OnClick := ButtonClick; // hier rummst es, und das soll es nicht end;
Delphi-Quellcode:
findet der Compiler total blöde :(
T.Create(nil);
Sinn und Zweck dieser Übung ist das Binding von Komponenten einer Form zu einer Controller-Unit. Dabei soll es die Controller-Unit aushalten, wenn auf der Form nicht alle Komponenten vorhanden sind. (Darum hält TGenericLink eben ein Dummy-Object vor, auf das zugegriffen wird, wenn kein gültiger Link gebildet werden konnte). Wer kann mir da mal auf den lahmen Gaul helfen? Die ganze Unit mal zum überfliegen:
Delphi-Quellcode:
unit FormBinding;
interface uses Classes; type IGenericLink<T: TComponent, Constructor> = interface ['{7650F483-5FDD-431F-96D1-65536B127BB5}'] function GetLink: T; procedure SetLink(const Value: T); property Link: T read GetLink write SetLink; procedure LinkTo(const Value: TComponent); overload; procedure LinkTo(const Owner: TComponent; const ComponentName: string); overload; function IsLinked: Boolean; function GetOnLinkChanged: TNotifyEvent; procedure SetOnLinkChanged(const Value: TNotifyEvent); property OnLinkChanged: TNotifyEvent read GetOnLinkChanged write SetOnLinkChanged; end; TGenericLink<T: TComponent, Constructor> = class(TInterfacedObject, IGenericLink<T>) private fInt: T; fExt: T; fOnLinkChanged: TNotifyEvent; function GetLink: T; procedure SetLink(const Value: T); function GetOnLinkChanged: TNotifyEvent; procedure SetOnLinkChanged(const Value: TNotifyEvent); public constructor Create; destructor Destroy; override; property Link: T read GetLink write SetLink; procedure LinkTo(const Value: TComponent); overload; procedure LinkTo(const Owner: TComponent; const ComponentName: string); overload; function IsLinked: Boolean; property OnLinkChanged: TNotifyEvent read GetOnLinkChanged write SetOnLinkChanged; end; implementation { TGenericLink<T> } constructor TGenericLink<T>.Create; begin inherited Create; fInt := T.Create; // <<-- ??? end; destructor TGenericLink<T>.Destroy; begin fInt.Free; inherited; end; function TGenericLink<T>.GetLink: T; begin if Assigned(fExt) then Result := fExt else Result := fInt; end; function TGenericLink<T>.GetOnLinkChanged: TNotifyEvent; begin Result := fOnLinkChanged; end; function TGenericLink<T>.IsLinked: Boolean; begin Result := Assigned(fExt); end; procedure TGenericLink<T>.LinkTo(const Owner: TComponent; const ComponentName: string); begin LinkTo(Owner.FindComponent(ComponentName)); end; procedure TGenericLink<T>.LinkTo(const Value: TComponent); begin if Assigned(Value) and Value.InheritsFrom(T) then SetLink(Value) else SetLink(nil); end; procedure TGenericLink<T>.SetLink(const Value: T); begin fExt := Value; if Assigned(OnLinkChanged) then OnLinkChanged(Self); end; procedure TGenericLink<T>.SetOnLinkChanged(const Value: TNotifyEvent); begin fOnLinkChanged := Value; end; end. |
AW: Generics: Instanz-Erzeugung in generischer Klasse
Wie ist's hiermit
Delphi-Quellcode:
oder
constructor TGenericLink<T>.Create;
begin inherited Create; fInt := T(TComponent.Create(nil)); // <<-- ??? end;
Delphi-Quellcode:
Ich meine, ich hätte das so schonmal probiert und es hat geklappt... aber keine Gewähr, ist schon spät :wink:
constructor TGenericLink<T>.Create;
begin inherited Create; fInt := T(TComponent(T).Create(nil)); // <<-- ??? end; |
AW: Generics: Instanz-Erzeugung in generischer Klasse
Wenn mans etwas dynamischer haben will:
Delphi-Quellcode:
constructor TTestClass<T>.create;
var f : T; Info : PTypeInfo; begin Info := TypeInfo(T); f := T(GetTypeData(Info)^.ClassType.Create); end; |
AW: Generics: Instanz-Erzeugung in generischer Klasse
Hmm, ich hätte auch gedacht, dass T in dem Fall TButton entspricht und entsprechend T.Create(nil) möglich sein müsste.
Ist das dann als Bug bzw. fehlendes Feature anzusehen oder geht das Problem vom Konzept her völlig in Ordnung? |
AW: Generics: Instanz-Erzeugung in generischer Klasse
Also, du mußt auf jeden Fall "irgendwie" das virtuelle
Delphi-Quellcode:
der TComponents aufrufen.
Create(...)
Das ist eben ein krankes Problem der Generics ... du hast zwar gesagt, es sollen nur TComponent und Nachfolger sein, aber scheinbar bieten dir die Generigs nur TObject und Nachfolger an, beim Zugriff auf Methoden, also nur das standardmäßige Create des TObjekts (ohne Vererbung). Ich würde auch zu
Delphi-Quellcode:
tendieren. (wenn ich nicht ständig Probleme bei Typecasts hätte)
TComponentClass(T).Create(nil)
Das
Delphi-Quellcode:
geht jedenfalls genauso wenig, da es ebenso wieder nur das Standard-TObject-Create aufruft.
T(GetTypeData(Info)^.ClassType.Create);
Wenn, dann muß man sich über die RTTI schon das richtige Create besorgen. Wäre ja schön gewesen, wenn man in TObject das Create virtuell gemacht hätte, dann könnte man dort das Standard-Create auf das entsprechende Create der entsprechenden Komponentenklasse umleiten können. (bei TComponent hätte man
Delphi-Quellcode:
auf
Create
Delphi-Quellcode:
weiterleiten können, wärend bei "ältesten"
Create(nil)
Delphi-Quellcode:
dann auf
Create(nil)
Delphi-Quellcode:
geleitet)
inherited Create
|
AW: Generics: Instanz-Erzeugung in generischer Klasse
Jo, merci erst mal ... obschon das alles nicht zum Ziel geführt hat ... mich dünkt wohl auch warum ...
Zitat:
Nachfahren von TComponent können aber auch den Constructor überschreiben:
Delphi-Quellcode:
und schon gibt es für diese Ableitung kein
TMyComponent = class(TComponent)
public constructor Create; reintroduce; end;
Delphi-Quellcode:
mehr.
Create(AOwner:TComponent)
Womöglich ist das so nicht anders machbar, aber da ich auch keine Lust habe für jedwede Ableitung von TComponent ein Interface zu implementieren, behelfe ich mir nun so, dass ich einfach eine Dummy-Instanz beim Erzeugen mitgebe. (Diese Dummy-Instanz wird dann am Schluss von selbiger einfach ins Nirwana geschickt)
Delphi-Quellcode:
Nicht schön, aber funktioniert :stupid:
var
MyButton : IGenericLink<TButton>; begin MyButton := TGenericLink<TButton>.Create( TButton.Create( nil ) ); MyButton.Link.OnClick := ButtonClick; // jetzt rummst auch nix mehr ... logisch end; |
AW: Generics: Instanz-Erzeugung in generischer Klasse
Zitat:
Zitat:
Was mich ja eher irritiert, ist dass das der Compiler TButton hier überhaupt zulässt. Ich bin jetzt kein experte in Delphi-Generics, aber
Delphi-Quellcode:
bedeutet doch wohl, dass T einen parameterlosen Konstruktor haben muss. Und TButton hat AFAIR keinen. Das sollte der Compiler doch eigentlich bemeckern. Oder wo ist mein Denkfehler?
TGenericLink<T: TComponent, Constructor>
Im übrigen gibt es mit ziemlicher Sicherheit eine schönere Lösung, als dein Workaround. Vermutlich über RTTI. Sowas in der Art macht Delphi ja bei den Formulardaten, die aus den dfms gelesen werden. Auch da müssen TComponent-Nachfahren dynamisch erzeugt werden, ohne, dass die konkrete Klasse zur Designzeit schon klar wäre. mfg Christian |
AW: Generics: Instanz-Erzeugung in generischer Klasse
Zitat:
Manchmal muss man, wenn man weiß, dass T von einem bestimmten Typ ist (da man ihn ja als Type constraint angegeben hat) trotzdem noch herumtricksen, da man nicht einfach alle Methoden dieses Typs benutzen kann.
Delphi-Quellcode:
geht auf jeden Fall nicht.
TComponent(T).Create(nil)
Das hier geht:
Delphi-Quellcode:
type
TGenericLink<T: TComponent> = class private FLink: T; function CreateComponent(AClass: TComponentClass): TComponent; public constructor Create; property Link: T read FLink; end; function TGenericLink<T>.CreateComponent(AClass: TComponentClass): TComponent; begin FLink := T(AClass.Create(nil)); end; constructor TGenericLink<T>.Create; begin CreateComponent(TComponentClass(T)); end; Zitat:
|
AW: Generics: Instanz-Erzeugung in generischer Klasse
@Stevie
You Made My Day :thumb: |
AW: Generics: Instanz-Erzeugung in generischer Klasse
Always a pleasure ;)
Mir fiel gerade noch auf, dass es doch mit nem "einfachen" cast ohne den Umweg über die extra Methode geht:
Delphi-Quellcode:
constructor TGenericLink<T>.Create;
begin FLink := T(TComponentClass(T).Create(nil)); end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:18 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz