Могу ли я создать конструктор, который десериализует строковую версию моего объекта? - PullRequest
5 голосов
/ 18 января 2011

Я сериализую и десериализую объект (потомок TComponent), используя пример в разделе ComponentToString в файле справки Delphi.Поэтому я могу сохранить объект в поле VARCHAR в базе данных.

Когда мне нужно создать экземпляр нового экземпляра моего класса из строки, хранящейся в базе данных, могу ли я сделать это с помощью конструктораформа CreateFromString(AOwner: TComponent; AData: String)?Или мне нужно использовать метод, не относящийся к классу, который возвращает экземпляр класса моего компонента?

Если я могу использовать версию конструктора, как мне "сопоставить" возвращаемое значение ReadComponent с "self"что создается конструктором?

Вот пример десериализации из файла справки:

function StringToComponentProc(Value: string): TComponent;
var
  StrStream:TStringStream;
  BinStream: TMemoryStream;
begin
  StrStream := TStringStream.Create(Value);
  try
    BinStream := TMemoryStream.Create;
    try
      ObjectTextToBinary(StrStream, BinStream);
      BinStream.Seek(0, soFromBeginning);
      Result:= BinStream.ReadComponent(nil);
    finally
      BinStream.Free;
    end;
  finally
    StrStream.Free;
  end;
end;

Ответы [ 3 ]

6 голосов
/ 18 января 2011

В общем, да, вы можете заставить конструктор десериализовать строку и использовать эту информацию для инициализации нового экземпляра. Тривиальным примером этого может быть класс с одним полем Integer. Передайте строку конструктору, вызовите конструктор StrToInt и инициализируйте поле с результатом.

Но если единственная функция, которую вы используете для десериализации, это та, которая также создает экземпляр, вы не можете использовать это из конструктора, потому что тогда у вас будет два экземпляра, когда вы захотите только одну. Для конструктора нет никакого способа сказать: «Не бери в голову, не создавай экземпляр в конце концов. У меня уже есть другой».

Однако, это не та ситуация, в которой вы находитесь. Как вы должны знать, TStream.ReadComponent позволяет вам создать экземпляр самостоятельно. Он создает экземпляр класса, только если вы еще не дали ему экземпляр для использования. Вы должны иметь возможность написать свой конструктор так:

constructor TLarryComponent.CreateFromString(const AData: string);
var
  StrStream, BinStream: TStream;
begin
  Create(nil);
  StrStream := TStringStream.Create(AData);
  try
    BinStream := TMemoryStream.Create;
    try
      ObjectTextToBinary(StrStream, BinStream);
      BinStream.Position := 0;
      BinStream.ReadComponent(Self);
    finally
      BinStream.Free;
    end;
  finally
    StrStream.Free;
  end;
end;

Там мы передаем текущий объект, обозначенный Self, ReadComponent. Поток будет игнорировать имя класса, хранящееся в потоке, и предположит, что текущий объект имеет правильный класс.

3 голосов
/ 18 января 2011

Вы можете сделать это с помощью class (статического) метода, но не через constructor.Конструкторы Delphis вызываются встроенным компилятором только что созданного экземпляра, который уже частично инициализирован (это искомый класс, а хранилище экземпляра / поля обнуляется).

Если вы видите источник TStream.ReadComponent, вы обнаружите, что реальный класс компонентов сначала читается из исходного потока, затем создается пустой экземпляр и заполняется RTTI из потока и возвращается как результат.Это означает:

Чтобы использовать TStream.ReadComponent, вам необходимо зарегистрировать свой класс в потоковой системе Delphis через RegisterClass.

3 голосов
/ 18 января 2011

Используйте статический class function вместо constructor:

type
  TYourClass = class(TComponent)
  public
    class function CreateFromString(AOwner: TComponent; AData: String): TYourClass; static;
  end;

implementation

class function TYourClass.CreateFromString(AOwner: TComponent; AData: String): TYourClass;
begin
   Result := (StringToComponentProc(AData) as TYourClass);
   if AOwner <> nil then
     AOwner.InsertComponent(Result);
end;

Однако может возникнуть проблема с частью AOwner, поскольку TStream.ReadComponent не имеет параметров для владельца.

Есть еще один вопрос об этой проблеме:

Как я могу указать владельца компонента, считываемого из Delphi TStream?

Редактировать: Я также обновил пример кода, добавив в него владельца.

Обратите внимание, что для вставки в список компонентов владельца требуется уникальный или пустой Name для вставляемого компонента.

...