2015-09-04 20 views
5

Sto cercando di generalizzare la convalida del contenuto dei componenti visivi con la proprietà Text utilizzando RTTI, ma quando provo a passare un valore stringa in TRttiMethod.Invoke, ottengo il messaggio "Typecast non valido". (In realtà "Ungültige Typumwandlung", ma suppongo, era una traduzione adeguata.)Come utilizzare una stringa in TRttiMethod.Invoke come parametro correttamente?

Il codice sotto è spogliato di tutte le misure di sicurezza, asserzioni e così via, assumendo che tutti gli oggetti passati siano semplicemente perfetti.

procedure ValidateTextFieldAndSetFocus(const Field: TObject; const Validator: TObject; const errorStates: array of TStringValidationResult; const sErrorMessage: string); 
var 
    context : TRttiContext; 
    objField : TRttiType; 
    objValid : TRttiType; 
    prop  : TRttiProperty; 
    execute : TRttiMethod; 
    I  : Integer; 
    validResult : TStringValidationResult; 
    value : TValue; 
begin 
    context := TRttiContext.Create; 
    objField := context.GetType(Field.ClassInfo); 
    objValid := context.GetType(Validator.ClassInfo); 
    prop  := objField.GetProperty('Text'); 
    value := prop.GetValue(Field); 
    execute := objValid.GetMethod('Execute'); 
    for I := 0 to High(errorStates) do 
    if execute.Invoke(Validator,[value]).TryAsType<TStringValidationResult>(validResult) then 
     if validResult = errorStates[I] then 
     begin 
     SetFocusIfCan(Field); 
     raise Exception.Create(sErrorMessage); 
     end; 
end; 

Il Validator Execute ha solo un parametro di stringa. Ho visto esempi in cui le stringhe sono state passate direttamente nella matrice di TValue, ma poi ho lo stesso errore typecast.

edit:

L'errore attuale appare in execute.Invoke(Validator,[value]).

Esempio

TNoSemicolonNullValidator = class 
    class function Execute(const aStr: string): TStringValidationResult; 
end; 

procedure TestValidation; 
var 
    Validator : TNoSemicolonNullValidator; 
begin 
    Validator := TNoSemicolonNullValidator.Create; 
    try 
    ValidateTextFieldAndSetFocus(Edit1,Validator,[svInvalid],'Edit1 is invalid!'); 
    finally 
    Validator.Free; 
    end; 
end; 
+0

prega di fornire una [MCVE] in modo che non abbiamo di indovinare le parti che mancano –

+0

Così com'è, questa domanda dovrebbe essere chiusa come fuori tema. Perché non hai fornito una riproduzione. Una volta risolto questo problema (vedi il link nel commento sopra), saremo in grado di rispondere. –

+0

In realtà il codice fornisce informazioni sufficienti per individuare l'errore. –

risposta

13

Si sta chiamando una funzione di classe qui, ma si passa un TObject come primo parametro (che è il Sé argomento nascosto di metodi non statici). Su un metodo di classe il parametro Self non deve essere un'istanza ma la classe di esso. Quindi la chiamata corretta sarebbe:

execute.Invoke(validator.ClassType, [value]); 

Ecco un esempio minimo per dimostrare che:

program Project1; 

{$APPTYPE CONSOLE} 

uses 
    Rtti, 
    SysUtils; 

type 
    TValidator = class 
    class function Execute(const s: string): Boolean; 
    end; 

class function TValidator.Execute(const s: string): Boolean; 
begin 
    Writeln(s); 
end; 

var 
    ctx: TRttiContext; 
    v: TValidator; 
begin 
    v := TValidator.Create; 
    try 
    ctx.GetType(TValidator).GetMethod('Execute').Invoke(v, ['test']); 
    except 
    on E: Exception do 
     Writeln(E.ClassName, ': ', E.Message); 
    end; 
    try 
    ctx.GetType(TValidator).GetMethod('Execute').Invoke(v.ClassType, ['test']); 
    except 
    on E: Exception do 
     Writeln(E.ClassName, ': ', E.Message); 
    end; 
    Readln; 
end.