Подтвердить что ты не робот

Правильный способ дублирования объекта Delphi

Каковы плюсы и минусы дублирования экземпляра объекта с конструктором или функцией экземпляра?

Пример A:

type
  TMyObject = class
  strict private
    FField: integer; 
  public
    constructor Create(srcObj: TMyObject); overload; 
    //alternatively:
    //constructor CreateFrom(srcObj: TMyObject);
    property Field: integer read FField;
  end;

constructor TMyObject.Create(srcObj: TMyObject);
begin
  inherited Create;
  FField := srcObj.Field;
end;

Пример B:

type
  TMyObject = class
  strict private
    FField: integer; 
  public
    function Clone: TMyObject;
    property Field: integer read FField;
  end;

function TMyObject.Clone: TMyObject;
begin
  Result := TMyObject.Create;
  Result.FField := FField;
end;

Одно важное различие сразу приходит в голову - в последнем случае конструктор Create должен быть виртуальным, чтобы иерархия классов, поддерживающая Clone, могла быть построена на основе TMyObject.

Предположим, что это не проблема - что TMyObject и все, что на нем основано, полностью под моим контролем. Каков ваш предпочтительный способ создания конструктора копирования в Delphi? Какую версию вы считаете более читаемой? Когда вы будете использовать прежний или последний подход? Обсудить.:)

EDIT: Моя главная проблема с первым примером заключается в том, что использование очень тяжело по сравнению со вторым подходом, то есть

newObj := TMyObject.Create(oldObj)

против.

newObj := oldObj.Clone;

EDIT2 или "Почему мне нужна однострочная операция"

Я согласен с тем, что Assign - разумный подход в большинстве случаев. Даже разумно реализовать "конструктор копирования" внутри, просто используя назначение.

Обычно я создаю такие копии при многопоточности и передаче объектов через очередь сообщений. Если создание объекта происходит быстро, я обычно передаю копию исходного объекта, потому что это действительно упрощает проблемы владения объектами.

IOW, я предпочитаю писать

Send(TMyObject.Create(obj));

или

Send(obj.Clone);

к

newObj := TMyObject.Create;
newObj.Assign(obj);
Send(newObj);
4b9b3361

Ответ 1

Первое добавляет информацию о том, какой объект хочет создать, а второй нет. Это можно использовать для создания экземпляра, например. потомок или предка класса

Способ Delphi (TPersistent) отделяет создание и клонирование:

dest := TSomeClass.Create; 
dest.Assign(source);  

и имеет такое же свойство, что вы явно выбираете класс для создания экземпляра. Но вам не нужны два конструктора, один для обычного использования, и тот, который вы хотите клонировать.

изменить из-за требования oneline Вы можете смешивать его, конечно, используя метаклассы Delphi (непроверенные)

type
  TBaseSomeObject = class;
  TBaseObjectClass = class of TBaseSomeObject;

  TBaseSomeObject = class(TPersistent)
    function Clone(t: TBaseObjectClass = nil): TBaseSomeObject; virtual;
  end;

...

  function TBaseSomeObject.Clone(t: TBaseObjectClass = nil): TBaseSomeObject;
  begin
    if Assigned(t) then
      Result := t.Create
    else
      Result := TBaseObjectClass(Self.ClassType).Create;
    Result.Assign(Self);
  end;


 SendObject(obj.Clone); // full clone.
 SendObject(obj.Clone(TDescandantObject)); // Cloned into Descendant object 

В остальном просто реализуйте свои операторы assign(), и вы можете смешивать несколько способов.

edit2

Я заменил код выше кодом, протестированным в D2009. Существуют некоторые зависимости типов, которые могли бы смутить вас, надеюсь, что это более понятно. Конечно, вам придется изучить механизм назначения. Я также тестировал параметр metaclass=nil по умолчанию, и он работает, поэтому я добавил его.

Ответ 2

Я не думаю, что есть правильный способ, это просто зависит от личного стиля. (И, как заметил Марко, есть еще несколько способов.

  • Метод конструктора короткий, но он нарушает принцип, согласно которому конструктор должен только конструировать объект. Возможно, это не проблема.
  • Способ клонирования является коротким, хотя вам необходимо предоставить вызов для каждого класса.
  • Назначение - это больше Delphi. Он отделяет создание и инициализацию, что хорошо, потому что нам нравится один метод, который позволяет использовать код лучше для поддержания.

И если вы реализуете Assign using streams, у вас есть только одно место, чтобы беспокоиться о том, какие поля должны быть доступны.

Ответ 3

Мне нравится стиль клона, но только на Java (или на любом другом языке GC). Я использовал его несколько раз в Delphi, но в основном я остаюсь с Create и Assign, потому что гораздо яснее, кто несет ответственность за уничтожение объекта.