Функция класса, которая создает экземпляр себя в Delphi - PullRequest
7 голосов
/ 29 сентября 2011

Можете ли вы иметь функцию класса, которая создает экземпляр класса:

TMyClass = class(TSomeParent)
public
  class function New(AValue : integer) : TMyClass; 
end;

TDerivedClass = class(TMyClass)
public
  function Beep;
end;

и затем использует его следующим образом

...   
var
  myList : TList<T>;
  item : TDerivedClass;
begin
  myList.Add(TDerivedClass.New(1))
  myList.Add(TDerivedClass.New(3))
  myList.Add(TDerivedClass.New(5))

  for item in myList do
    item.Beep; //times the count in the class function
...

И если да, то что делает этот код функциивыглядит как?Используете ли вы метод NewInstance TObject, и вы каждый раз повторно реализуете для каждого производного класса?Безопаснее / лучше использовать конструктор?

Цель состоит в том, чтобы использовать этот подход в шаблоне команд и загрузить список команд с типами классов и получателем, например:

//FYI: document is an instance of TDocument
commandList.Execute(TOpenDocument(document)); 
commandList.Execute(TPasteFromClipboard(document)); 
//... lots of actions - some can undo
commandList.Execute(TPrintDocument(document)); 
commandList.Execute(TSaveDocument(document));

ИПричина этого заключается в том, что некоторые команды будут указываться с помощью текста / скрипта и должны быть разрешены во время выполнения.

Ответы [ 5 ]

12 голосов
/ 29 сентября 2011

То, что вы ищете, называется фабричным шаблоном .Это можно сделать в Delphi;это то, как VCL десериализует формы, между прочим.Что вам не хватает, так это регистрационная / поисковая часть системы.Вот основная идея:

  • Где-то вы создали таблицу регистрации.Если вы используете Delphi XE, вы можете реализовать это как TDictionary<string, TMyClassType>, где TMyClassType определяется как class of TMyClass.Это важно.Вам нужна карта между именами классов и ссылками на типы классов.
  • Поместите виртуальный конструктор в TMyClass.Все, что происходит от него, будет использовать этот конструктор или его переопределение, когда его создаст шаблон фабрики.
  • Когда вы создаете новый класс-потомок, пусть он вызывает метод, который будет регистрироваться в таблице регистрации.,Это должно происходить при запуске программы, либо в initialization , либо в конструкторе класса.

Когда вам нужно создать экземпляр чего-либо из скрипта, сделайте это следующим образом:

 class function TMyClass.New(clsname: string; [other params]): TMyClass;
 begin
   result := RegistrationTable[clsName].Create(other params);
 end;

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

5 голосов
/ 29 сентября 2011

Да, технически возможно создать экземпляр из метода класса, просто вызвать фактический конструктор и затем вернуть экземпляр, который он создает, например:

type
  TMyClass = class(TSomeParent)
  public
    constructor Create(AValue : Integer); virtual;
    class function New(AValue : integer) : TMyClass;
  end;

  TDerivedClass = class(TMyClass)
  public
    constructor Create(AValue : Integer); override;
    function Beep;
  end;

constructor TMyClass.Create(AValue : Integer);
begin
  inherited Create;
  ...
end;

function TMyClass.New(AValue : integer) : TMyClass;
begin
  Result := Create(AValue);
end;

constructor TDerivedClass.Create(AValue : Integer);
begin
  inherited Create(AValue);
  ...
end;

var
  myList : TList<TMyClass>;
  item : TMyClass;
begin
  myList.Add(TDerivedClass.New(1))
  myList.Add(TDerivedClass.New(3))
  myList.Add(TDerivedClass.New(5))
  for item in myList do
    TDerivedClass(item).Beep;

В этом случае вам лучше просто использовать конструктор напрямую:

type
  TMyClass = class(TSomeParent)
  end;

  TDerivedClass = class(TMyClass)
  public
    constructor Create(AValue : Integer);
    function Beep;
  end;

var
  myList : TList<TDerivedClass>;
  item : TDerivedClass;
begin
  myList.Add(TDerivedClass.Create(1))
  myList.Add(TDerivedClass.Create(3))
  myList.Add(TDerivedClass.Create(5))
  for item in myList do
    item.Beep;
2 голосов
/ 29 сентября 2011

Можете ли вы иметь функцию класса, которая создает экземпляр класса.
Безопаснее / лучше использовать конструктор?

Конструктор - это функция класса, которая создает экземпляр класса.Просто положите:

constructor New(); virtual;

И вам пора.

Часть virtual; позволит вам вызывать один и тот же конструктор New () для всех классов-потомков.

1 голос
/ 31 января 2014

Другой вариант - использовать RTTI.Приведенный ниже код работает как обычный метод в моем классе как способ получения нового экземпляра объекта с подмножеством элементов, но поскольку элементы (вместе с самим объектом списка), вероятно, являются потомками, создавая экземпляробъект, в котором определен метод, недостаточно хорош, поскольку он должен быть того же типа, что и экземпляр.

т.е.

TParentItem = Class
End;

TParentList = Class
  Items : TList<TParentItem>;
  Function GetSubRange(nStart,nEnd : Integer) : TParentList;
End;

TChildItem = Class(TParentItem)
end

TChildList = Class(TParentList)
end

List := TChildList.Create;
List.LoadData;

SubList := List.GetSubRange(1,3);

Реализация, если GetSubRange будет выглядеть примерно так...

Function TParentList.GetSubRange(nStart,nEnd : Integer) : TParentList;
var
  aContext: TRttiContext;
  aType: TRttiType;
  aInsType : TRttiInstanceType;
  sDebug : String;
begin
  aContext := TRttiContext.Create;
  aType := aContext.GetType(self.ClassType);
  aInsType := aType.AsInstance;
  Result := aInsType.GetMethod('Create').Invoke(aInsType.MetaclassType,[]).AsType<TParentList>;
  sDebug := Result.ClassName; // Should be TChildList

  // Add the items from the list that make up the subrange.
End;

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

0 голосов
/ 29 сентября 2011

Вы должны использовать конструктор (особый «вид» функции класса). TObject.NewInstance не подходит, если вам не требуется специальное выделение памяти.

А что касается подпрограммы «Выполнение» из списка команд: теперь выполняемое действие зависит от типа объекта. Представьте, что документ можно открывать, печатать, вставлять и сохранять одновременно (не странное предположение), что будет трудно реализовать в этой структуре. Вместо этого рассмотрите возможность добавления интерфейсов (IOpenDocument, IPasteFromClipboard, IPrintable, ISaveDocument), которые действительно могут быть действиями одного экземпляра документа.

...