У меня есть класс в моем приложении Delphi, где мне нужен простой и динамичный способ сброса всех свойств строки в '' и все логические свойства в False Насколько я могу видеть в Интернете, должно быть возможно сделать какую-то петлю, но мне это не понятно.
Как закодировать все свойства в классе
Ответ 1
Обратите внимание, что следующий код работает только для опубликованных свойств класса! Кроме того, экземпляр класса, переданного функции ниже, должен иметь как минимум опубликованный раздел!
Вот как установить опубликованные значения свойств строки в пустую строку и логические значения в False, используя старый стиль RTTI.
Если у вас Delphi старше Delphi 2009, вам может быть не указан тип tkUString. Если это так, просто удалите его из следующего кода:
uses
TypInfo;
procedure ResetPropertyValues(const AObject: TObject);
var
PropIndex: Integer;
PropCount: Integer;
PropList: PPropList;
PropInfo: PPropInfo;
const
TypeKinds: TTypeKinds = [tkEnumeration, tkString, tkLString, tkWString,
tkUString];
begin
PropCount := GetPropList(AObject.ClassInfo, TypeKinds, nil);
GetMem(PropList, PropCount * SizeOf(PPropInfo));
try
GetPropList(AObject.ClassInfo, TypeKinds, PropList);
for PropIndex := 0 to PropCount - 1 do
begin
PropInfo := PropList^[PropIndex];
if Assigned(PropInfo^.SetProc) then
case PropInfo^.PropType^.Kind of
tkString, tkLString, tkUString, tkWString:
SetStrProp(AObject, PropInfo, '');
tkEnumeration:
if GetTypeData(PropInfo^.PropType^)^.BaseType^ = TypeInfo(Boolean) then
SetOrdProp(AObject, PropInfo, 0);
end;
end;
finally
FreeMem(PropList);
end;
end;
Вот простой тестовый код (обратите внимание, что свойства должны быть опубликованы, если в классе нет опубликованных свойств, должен быть хотя бы пустой опубликованный раздел):
type
TSampleClass = class(TObject)
private
FStringProp: string;
FBooleanProp: Boolean;
published
property StringProp: string read FStringProp write FStringProp;
property BooleanProp: Boolean read FBooleanProp write FBooleanProp;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
SampleClass: TSampleClass;
begin
SampleClass := TSampleClass.Create;
try
SampleClass.StringProp := 'This must be cleared';
SampleClass.BooleanProp := True;
ResetPropertyValues(SampleClass);
ShowMessage('StringProp = ' + SampleClass.StringProp + sLineBreak +
'BooleanProp = ' + BoolToStr(SampleClass.BooleanProp));
finally
SampleClass.Free;
end;
end;
Ответ 2
если вы являетесь пользователем Delphi 2010 (и выше), тогда есть новый модуль RTTI (rtti.pas). вы можете использовать его для получения информации о времени выполнения для вашего класса и его свойств (общедоступные свойства по умолчанию, но вы можете использовать директиву {$RTTI}
для включения информации о защищенных и закрытых полях).
Например, у нас есть следующий тестовый класс с 3 общедоступными полями (1 логическое и 2 строковых поля (один из них только для чтения)).
TTest = class(TObject)
strict private
FString1 : string;
FString2 : string;
FBool : boolean;
public
constructor Create();
procedure PrintValues();
property String1 : string read FString1 write FString1;
property String2 : string read FString2;
property BoolProp : boolean read FBool write FBool;
end;
constructor TTest.Create();
begin
FBool := true;
FString1 := 'test1';
FString2 := 'test2';
end;
procedure TTest.PrintValues();
begin
writeln('string1 : ', FString1);
writeln('string2 : ', FString2);
writeln('bool: ', BoolToStr(FBool, true));
end;
чтобы перечислять все свойства объекта и устанавливать его значения по умолчанию, вы можете использовать что-то вроде кода ниже.
Во-первых, вы должны инициализировать структуру TRttiContext
(это не обязательно, потому что это запись). Затем вы должны получить информацию rtti о своем obejct, после чего вы можете зацикливать свои свойства и фильтровать их (пропустить свойства readonly и другие, кроме boolean и stirng). Учтите, что существует несколько типов строк: tkUString, tkString и другие (посмотрите TTypeKind
в typinfo.pas
)
TObjectReset = record
strict private
public
class procedure ResetObject(obj : TObject); static;
end;
{ TObjectReset }
class procedure TObjectReset.ResetObject(obj: TObject);
var ctx : TRttiContext;
rt : TRttiType;
prop : TRttiProperty;
value : TValue;
begin
ctx := TRttiContext.Create();
try
rt := ctx.GetType(obj.ClassType);
for prop in rt.GetProperties() do begin
if not prop.IsWritable then continue;
case prop.PropertyType.TypeKind of
tkEnumeration : value := false;
tkUString : value := '';
else continue;
end;
prop.SetValue(obj, value);
end;
finally
ctx.Free();
end;
end;
простой код для проверки:
var t : TTest;
begin
t := TTest.Create();
try
t.PrintValues();
writeln('reset values'#13#10);
TObjectReset.ResetObject(t);
t.PrintValues();
finally
readln;
t.Free();
end;
end.
и результат
string1 : test1
string2 : test2
bool: True
reset values
string1 :
string2 : test2
bool: False
также взгляните на атрибуты, imo неплохо пометить свойства (которые вам нужны reset) с некоторым атрибутом, и может быть со значением по умолчанию, например:
[ResetTo('my initial value')]
property MyValue : string read FValue write FValue;
тогда вы можете фильтровать только свойства, отмеченные ResetToAttribute