Приведение TObject с использованием его ClassType?
Как я могу заставить мой код работать?:) Я пытался сформулировать этот вопрос, но после нескольких неудачных попыток я думаю, что вы, ребята, заметите проблему быстрее, глядя на код, чем читая мои "объяснения". благодарю вас.
setCtrlState([ memo1, edit1, button1], False);
_
procedure setCtrlState(objs: array of TObject; bState: boolean = True);
var
obj: TObject;
ct: TClass;
begin
for obj in objs do
begin
ct := obj.ClassType;
if (ct = TMemo) or (ct = TEdit) then
ct( obj ).ReadOnly := not bState; // error here :(
if ct = TButton then
ct( obj ).Enabled:= bState; // and here :(
end;
end;
5 ответов
Было бы проще использовать RTTI вместо явного приведения, то есть:
uses
TypInfo;
setCtrlState([ memo1, edit1, button1], False);
procedure setCtrlState(objs: array of TObject; bState: boolean = True);
var
obj: TObject;
PropInfo: PPropInfo;
begin
for obj in objs do
begin
PropInfo := GetPropInfo(obj, 'ReadOnly');
if PropInfo <> nil then SetOrdProp(obj, PropInfo, not bState);
PropInfo := GetPropInfo(obj, 'Enabled');
if PropInfo <> nil then SetOrdProp(obj, PropInfo, bState);
end;
end;
Вы должны явно привести объект к некоторому классу. Это должно работать:
procedure setCtrlState(objs: array of TObject; bState: boolean = True);
var
obj: TObject;
ct: TClass;
begin
for obj in objs do
begin
ct := obj.ClassType;
if ct = TMemo then
TMemo(obj).ReadOnly := not bState
else if ct = TEdit then
TEdit(obj).ReadOnly := not bState
else if ct = TButton then
TButton(obj).Enabled := bState;
end;
end;
Это можно сократить с помощью "is
"оператор - нет необходимости в переменной ct:
procedure setCtrlState(objs: array of TObject; bState: boolean = True);
var
obj: TObject;
begin
for obj in objs do
begin
if obj is TMemo then
TMemo(obj).ReadOnly := not bState
else if obj is TEdit then
TEdit(obj).ReadOnly := not bState
else if obj is TButton then
TButton(obj).Enabled := bState;
end;
end;
Вам нужно привести объект ct к TMemo/TEdit/TButton, прежде чем вы сможете установить свойства объекта.
Строка, в которой вы получаете ошибки, ошибочна, потому что ct по-прежнему TClass, а не TButton/etc. Если вы приведете к TButton, вы сможете установить для параметра true значение true.
Я рекомендую прочитать о кастинге в Delphi. Лично я бы рекомендовал использовать операторы as/is вместо ClassType. В этом случае код будет проще и гораздо более понятен.
Лично я написал бы это больше как:
procedure setCtrlState(objs: array of TObject; bState: boolean = True);
var
obj: TObject;
begin
for obj in objs do
begin
// I believe these could be merged by using an ancestor of TMemo+TEdit (TControl?)
// but I don't have a good delphi reference handy
if (obj is TMemo) then
TMemo(obj).ReadOnly := not bState;
if (obj is TEdit) then
TEdit(obj).ReadOnly := not bState;
if (obj is TButton) then
TButton(obj).Enabled := bState;
end;
end;
Нет необходимости приводить к TMemo и TEdit по отдельности, поскольку они оба являются потомками общего родительского класса, у которого есть свойство ReadOnly:
procedure TForm1.FormCreate(Sender: TObject);
procedure P(const Obj: TComponent);
begin
if Obj is TCustomEdit then
TCustomEdit(Obj).ReadOnly := True;
end;
begin
P(Memo1);
P(Edit1);
end;
Вы можете избежать ссылок на различные юниты и явное приведение, если не возражаете против небольшого снижения производительности и ограничиваете изменения опубликованными свойствами. Посмотрите на блок TypInfo, включенный в Delphi.