Таблица виртуальных методов
Я использую этот блок:
procedure ExecMethod(Target: TClass; const MethodName: string; const Args: array of TValue);
var
LContext: TRttiContext;
LType: TRttiType;
LMethod: TRttiMethod;
begin
LType := LContext.GetType(Target);
for LMethod in LType.GetMethods do
if (LMethod.Parent = LType) and (LMethod.Name = MethodName) then begin
LMethod.Invoke(Target.Create, Args);
break;
end;
end;
как это:
ExecMethod(TFuncClass, 'Test1', []);
ExecMethod(TFuncClass, 'Test2', ['hey']);
ExecMethod(TFuncClass, 'Test3', [100]);
в этом классе:
TFuncClass = class(TObject)
published
procedure Test1;
procedure Test2(const str: string);
procedure Test3(i: integer);
// there's more, each one with different prototype
end;
var
FuncClass: TFuncClass;
но потом я продолжаю получать нарушения прав доступа... или недопустимый класс указателя приведения (или любой другой)..
1 ответ
Как я заметил в источнике вашего кода, он теряет память, потому что он создает экземпляры данного класса, даже не освобождая их. Это не должно вызывать каких-либо немедленных ошибок во время выполнения, поэтому это не является причиной проблемы под рукой.
Код вопроса обобщает исходный код для работы с любым конкретным классом и при этом становится технически неверным. Чтобы понять почему, вам нужно понять, как Delphi создает объекты из ссылок на классы:
Когда вы вызываете конструктор для переменной-ссылки на класс (как в Target.Create
), компилятор использует полученные знания, как во время компиляции, чтобы решить, какой конструктор вызвать. В этом случае целью вызова является TClass
и единственный конструктор, который знает компилятор, доступен для этого типа TObject.Create
это конструктор, который называется. Если TFuncClass
имеет некоторый другой конструктор - даже если он соответствует сигнатуре с нулевым аргументом, унаследованной от TObject
- это никогда не называется. Тип созданного объекта все равно будет отображаться как TFuncClass
хотя - то ClassType
функция вернется TFuncClass
и is
Оператор будет работать как положено.
Когда код вызывает неправильный конструктор для класса, он заканчивается недопустимым экземпляром нужного класса. Наличие недействительных экземпляров может привести к всевозможным проблемам. Я не удивлюсь, если это будет связано с нарушениями прав доступа, неверными приведениями типов, неверными результатами или чем-то еще.
Код, показанный в вопросе, не должен иметь проблемы, которую я описал, так как TFuncClass
не имеет нового конструктора Однако данный код явно неполон, поэтому, возможно, он был слишком упрощен для представления здесь.
Было бы гораздо лучше, если бы вызывающая сторона отвечала за предоставление экземпляра для вызова методов, например так:
procedure ExecMethod(Target: TObject; const MethodName: string; const Args: array of TValue);
var
LContext: TRttiContext;
LType: TRttiType;
LMethod: TRttiMethod;
begin
LType := LContext.GetType(Target.ClassType);
for LMethod in LType.GetMethods(MethodName) do
// TODO: Beware of overloaded methods
LMethod.Invoke(Target, Args);
end;
Используйте эту функцию так:
FuncClass := TFuncClass.Create(...);
try
ExecMethod(FuncClass, 'Test1', []);
ExecMethod(FuncClass, 'Test2', ['hey']);
ExecMethod(FuncClass, 'Test3', [100]);
finally
FuncClass.Free
end;
Обратите внимание, что все это предполагает, что второй параметр, строковое имя метода, фактически предоставляется некоторой переменной, значение которой неизвестно до времени выполнения. Если вы передаете строковый литерал в ExecMethod
тогда вам следует прекратить звонить ExecMethod
прекратите возиться с RTTI и просто вызовите нужный метод напрямую: FuncClass.Test2('hey')
,