RTTI: Posso ottenere un tipo per nome?
-
13-09-2019 - |
Domanda
Data una stringa di testo che contiene un nome di tipo, c'è qualche modo per ottenere il tipo appropriato per sé?
che sto cercando di fare qualcosa di simile:
type
TSomeType<T> = class
// yadda yadda
end;
procedure DoSomething;
var
obj : TObject;
begin
o := TSomeType<GetTypeByName('integer')>.Create;
// do stuff with obj
end;
Ho guardato diverse spiegazioni RTTI in linea e guardato attraverso le unità di Delphi e non vedo quello che sto cercando. È possibile?
Soluzione
No, i generici sono del tutto compiletime.
Altri suggerimenti
La nuova unità RTTI in Delphi 2010 ha un modo di recuperare i tipi dichiarati nella sezione di interfaccia di unità. Per ogni dato tipo, rappresentato da un'istanza TRttiType
, la proprietà TRttiType.QualifiedName
restituisce un nome che può essere utilizzato con TRttiContext.FindType
tardi per recuperare il tipo. Il nome completo è il nome dell'unità completo (incluso namespace, se esistono), seguito da un '', seguita dal nome di tipo completo (inclusi i tipi esterni se nested).
Quindi, si potrebbe recuperare una rappresentazione del tipo Integer (sotto forma di un TRttiType
) con context.FindType('System.Integer')
.
Ma questo meccanismo non può essere utilizzato per recuperare istanze di tipi generici che non sono stati istanziati in fase di compilazione; istanziazione in fase di esecuzione richiede la generazione di codice runtime.
Si può sempre registrare i tipi in una sorta di registro (gestito da un elenco di stringhe o un dizionario) e creare una funzione di fabbrica per poi tornare l'oggetto appropriato. Purtroppo si dovrebbe sapere in anticipo quali tipi che stavano per bisogno. Qualcosa di simile alle funzioni di Delphi RegisterClass e FindClass (nell'unità di classi). Il mio pensiero è quello di mettere il tipo di modello generico nella lista direttamente.
Un esempio di possibile utilizzo:
RegisterCustomType('Integer',TSomeType<Integer>);
RegisterCustomType('String',TSomeType<String>);
if FindCustomType('Integer') <> nil then
O := FindCustomType('Integer').Create;
Modifica Ecco una semplice implementazione specifica utilizzando un tDictionary da Generics.Collections per gestire la memorizzazione registro ... Lascio l'estrazione di questo in metodi utili come un semplice esercizio per il lettore.
var
o : TObject;
begin
TypeDict := TDictionary<String,TClass>.Create;
TypeDict.Add('integer',TList<integer>);
if TypeDict.ContainsKey('integer') then
o := TypeDict.Items['integer'].Create;
if Assigned(o) then
ShowMessage(o.ClassName);
end;
Un altro EDIT: stavo dando qualche pensiero questo la scorsa notte, e ha scoperto un'altra tecnica che è possibile unire in questo concetto. Interfacce. Ecco un esempio veloce non fare nulla, ma può essere facilmente esteso:
TYPE
ITest = interface
['{0DD03794-6713-47A0-BBE5-58F4719F494E}']
end;
TIntfList<t> = class(TList<T>,ITest)
public
function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
procedure TForm1.Button7Click(Sender: TObject);
var
o : TObject;
fTestIntf : ITest;
begin
TypeDict := TDictionary<String,TClass>.Create;
TypeDict.Add('integer',TIntfList<integer>);
if TypeDict.ContainsKey('integer') then
o := TypeDict.Items['integer'].Create;
if Assigned(o) and Supports(o,ITest,fTestIntf) then
ShowMessage(o.ClassName);
end;
Naturalmente si dovrà implementare i metodi QueryInterface, _AddRef e _Release ed estendere l'interfaccia di fare qualcosa di più utile.
Se si dimentica generici e tipi di base, la funzione "RegisterClass" sarebbe utile. Ma non funziona per i medicinali generici o tipi di base.