Как я могу создать новый экземпляр класса?

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

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

case MyObjectTypeInstance.MyTypeEnum of
  obj1:
    Result:=TObjectType1.Create;

  obj2:
    Result:=TObjectType2.Create;

  obj3:
    Result:=TObjectType3.Create;
end;

это не будет следовать принципу «открыто/закрыто».

изначально я думал, что могу сделать что-то вроде "Result:=MyObjectTypeInstance.Create;" но это не сработало из-за трудностей деструктора.

вот последнее предположение, как я должен это делать...

var
  fooA, fooB:TFoo;
begin
  fooA:=TFoo2.Create;    // it could be any of many types

  fooB:=?  // how to create fooB of same class type as fooA????

  // do something

  fooA.Free;
  fooB.Free;
end;

я бы подумал, что это будет проще!

Спасибо за помощь!


person X-Ray    schedule 16.04.2009    source источник


Ответы (5)


Вероятно, вы захотите создать класс Abstract Factory или Factory Method. Это распространенные Шаблоны проектирования, проверенные и проверенные парадигмы разработки.

person dar7yl    schedule 16.04.2009

Если все классы имеют общего предка, вы можете сделать что-то вроде этого:

type
  TAncestor = class;
  TAncestorClass = class of TAncestor;
  TAncestor = class 
  public
    constructor Create; virtual;

    class function CreateClass(const AId: string): TAncestor;
    class procedure RegisterClass(const AId: string; const AType: TAncestorClass);
  end;


class function TAncestor.CreateClass(const AId: string): TAncestor;
var
  atype : TAncestorClass;
begin
  atype := GetAncestorClass(AId);
  if atype<>nil then
    Result := atype.Create
  else
    Result := nil;
end;

class procedure TAncestor.RegisterClass(const AId: string; 
  const AType: TAncestorClass);
begin
  SetAncestorClass(AId, AType); // Link id to class type
end;

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

person Toon Krijthe    schedule 16.04.2009
comment
спасибо геймкот! на самом деле, спасибо за ваш огромный вклад в StackOverflow. - person X-Ray; 16.04.2009

Вариант 1 — создайте список сопоставлений имени/класса: Есть ли способ создать экземпляр класса по его имени в delphi?

Вариант 2 - использовать переменную класса.

type
  TBaseObj = class
  end;

  TObjA = class(TBaseObj)
  end;

  TBaseObjClass = class of TBaseObj;

var
  objCls: TBaseObjClass;
  obj: TBaseObj;

objCls := TObjA;
obj := objCls.Create;
//obj is of type TObjA
person gabr    schedule 16.04.2009
comment
спасибо габр! на самом деле, спасибо за ваш огромный вклад в StackOverflow. - person X-Ray; 16.04.2009

спасибо всем за ваши ответы!

Решение dar7yl идеально подходило для моих нужд.

type
  TFoo = class
  private
    { private declarations }
  public
    { public declarations }
    class function MakeAnother:TFoo;
  end;

  TFoo1 = class(TFoo)
  private
    { private declarations }
  public
    { public declarations }
  end;

  TFoo2 = class(TFoo)
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  fooA, fooB:TFoo;
begin
  fooA:=TFoo2.Create;
  foob:=fooA.MakeAnother;

  // do something here

  fooA.Free;
  fooB.Free;
end;

{ TFoo }

class function TFoo.MakeAnother: TFoo;
begin
  Result:=Create;
end;
person X-Ray    schedule 16.04.2009
comment
Спасибо за публикацию этого дополнения к вашему вопросу / примеру - очень полезно для скрытников и «позже» с похожими вопросами. :) - person Jamo; 17.04.2009
comment
пожалуйста. Я всегда рад видеть такие продолжения, поэтому я стараюсь делать их сам! - person X-Ray; 17.04.2009
comment
хм, если это то, как сейчас выглядит ваше решение, то, похоже, я неправильно понял вопрос тогда... Мне кажется, вы все еще явно указываете, какой класс создавать... - person Oliver Giesen; 17.04.2009
comment
мне нужен еще один экземпляр того же объекта. возможно, мой вопрос был не так велик. - person X-Ray; 17.04.2009

Другая, более беспорядочная версия использует «класс типа» и TObject.ClassType.

type
 TFoo = class
  private
    { private declarations }
  public
    { public declarations }
    constructor Create(WhatEver : Integer);virtual;// just to show need for params
  end;

  TFooClass = class of TFoo;

  TFoo1 = class(TFoo)
  private
    { private declarations }
  public
    { public declarations }
    constructor Create(WhatEver : Integer);override;// just to show need for params
  end;

  TFoo2 = class(TFoo)
  private
    { private declarations }
  public
    { public declarations }
  end;


{$R *.dfm}

procedure TForm10.Button1Click(Sender: TObject);
var
  fooA, fooB:TFoo;

begin
  fooA:=TFoo2.Create(0);
  fooB:= TFooClass(FooA.ClassType).Create(1);

  // do something here

  fooA.Free;
  fooB.Free;

end;

{ TFoo }

constructor TFoo.Create(WhatEver: Integer);
begin
  ShowMessageFmt('%s %d', [Self.ClassName, WhatEver]);
end;

{ TFoo1 }

constructor TFoo1.Create(WhatEver: Integer);
begin
  inherited;

end;
person Gerry Coll    schedule 17.04.2009
comment
Не назвал бы это беспорядочным, но довольно гибким :) Недавно я использовал его для создания функций-клонов, где базовый класс может создать правильный экземпляр дочернего класса. - person Heinrich Ulbricht; 16.06.2009
comment
Я думаю, что это истинный путь Delphi. Просто не забудьте сделать конструкторы виртуальными. Обратите внимание, что причина, по которой вы должны писать TFooClass(FooA.ClassType).Create вместо FooA.Create, заключается в том, что последний не будет выделять новый объект — именно так Delphi вызывает унаследованные конструкторы! - person Ian Goldby; 24.03.2011