如何正确使用 TRttiMethod.Invoke 中的字符串作为参数?
How do I use a string in TRttiMethod.Invoke as parameter properly?
我正在尝试使用 RTTI 通过 Text-属性 概括视觉组件的内容验证,但是当我尝试将字符串值传递到 TRttiMethod.Invoke 时,我收到消息 "Invalid Typecast"。 (实际上 "Ungültige Typumwandlung" 但我猜,这是一个合适的翻译。)
假设所有传递的对象都是完美的,下面的代码去除了所有安全措施、断言等。
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;
Validator的Execute只有一个string-Parameter。我见过将字符串直接传递到 TValue 数组的示例,但随后我遇到了相同的类型转换错误。
编辑:
实际错误出现在execute.Invoke(Validator,[value])
中。
例子
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;
您在这里调用了一个 class 函数,但是您传递了一个 TObject 作为第一个参数(这是非静态方法的隐藏的 Self 参数)。在 class 方法中,Self 参数不能是一个实例,而是它的 class。所以正确的调用是:
execute.Invoke(validator.ClassType, [value]);
这里有一个最小的例子来证明:
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.
我正在尝试使用 RTTI 通过 Text-属性 概括视觉组件的内容验证,但是当我尝试将字符串值传递到 TRttiMethod.Invoke 时,我收到消息 "Invalid Typecast"。 (实际上 "Ungültige Typumwandlung" 但我猜,这是一个合适的翻译。)
假设所有传递的对象都是完美的,下面的代码去除了所有安全措施、断言等。
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;
Validator的Execute只有一个string-Parameter。我见过将字符串直接传递到 TValue 数组的示例,但随后我遇到了相同的类型转换错误。
编辑:
实际错误出现在execute.Invoke(Validator,[value])
中。
例子
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;
您在这里调用了一个 class 函数,但是您传递了一个 TObject 作为第一个参数(这是非静态方法的隐藏的 Self 参数)。在 class 方法中,Self 参数不能是一个实例,而是它的 class。所以正确的调用是:
execute.Invoke(validator.ClassType, [value]);
这里有一个最小的例子来证明:
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.