I'm trying to generalize the content validation of visual components with the Text-property using RTTI but when I try to pass a string value into TRttiMethod.Invoke, I get the Message "Invalid Typecast". (Actually "Ungültige Typumwandlung" but I guess, that was a fitting translation.)
The code below is stripped of all security measures, assertions and so on, assuming all passed objects are just perfect.
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;
The Validator's Execute only has one string-Parameter. I've seen examples where strings were passed directly into the array of TValue, but then I get the same typecast error.
edit:
The actual error appears in execute.Invoke(Validator,[value])
.
Example
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;
You are calling a class function here but you are passing a TObject as first parameter (which is the hidden Self argument of non static methods). On a class method the Self parameter must not be an instance but the class of it. So the correct call would be:
execute.Invoke(validator.ClassType, [value]);
Here is a minimal example to prove that:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With