Как закодировать все свойства в классе

У меня есть класс в моем приложении 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