Можно ли сохранить тип в переменной в Delphi?

Например, у меня есть три класса (TMyClass1, TMyClass2 и TMyClass3).

У меня есть функция, которая принимает в качестве параметра строку с именем типа, а затем создает экземпляр этого класса с учетом типа. Прямо сейчас я использую операторы if, например:

if (AFormName = 'xxx1') then
begin
    MyClass := MyClass1.Create;
end else if (AFormName = 'xxx2') then        
begin
    MyClass := MyClass2.Create;
end;

Так далее..

Есть ли способ сохранить тип в переменной, чтобы изменить ссылку на строки на типы и не использовать это?

Если это поможет, MyClass1, MyClass2 и MyClass3 наследуются от другого класса...

да. Это называется ссылкой на класс. (связь).

Sertac Akyuz 10.05.2019 22:47

Например, type TMyTypeClass = class of TMyType; затем вы объявляете переменную var MyTypeClass: TMyTypeClass;, затем назначаете ее MyTypeClass := TMyClass1;, а затем фактически используете ее MyClass := MyTypeClass.Create();

Jerry Dodge 10.05.2019 23:15

И как мне связать строку с этой переменной типа?

nick 10.05.2019 23:18

Строки — это отдельная история. Вы не ссылаетесь по имени строки. Вы ссылаетесь на сам фактический тип. Почему вы хотите использовать строки для ссылки на классы? Возможно, вы сможете использовать RTTI, но это кажется излишним для такой простой задачи.

Jerry Dodge 10.05.2019 23:59

Используй словарь

David Heffernan 11.05.2019 09:35
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
5
401
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Посмотрите на функции RTL RegisterClass() и FindClass()/GetClass(), они были разработаны именно для такого сценария, например:

interface

uses
  ..., Classes;

type
  MyBase = class(TPersistent)
  public
    constructor Create; virtual;
  end;

  MyBaseClass = class of MyBase;

  MyClass1 = class(MyBase)
  public
    constructor Create; override;
  end;

  MyClass2 = class(MyBase)
  public
    constructor Create; override;
  end;

  MyClass3 = class(MyBase)
  public
    constructor Create; override;
  end;

...

function MakeMyClass(const Name: string): MyBase;

implementation

function MakeMyClass(const Name: string): MyBase;
begin
  Result := MyBaseClass(GetClass(Name)).Create;
end;

...

initialization
  RegisterClasses([MyClass1, MyClass2, MyClass3{, ...}]);

end.

Тогда вы можете сделать это:

var
  MyObj: MyBase;
begin
  MyObj := MakeMyClass('MyClass1');
  ...
  MyObj.Free;
end;

Недостатком является то, что классы должны быть производными от TPersistent. Если вы не хотите использовать TPersistent или просто хотите использовать разные имена для создания объектов класса, не очень сложно настроить пользовательскую фабрику, например, с помощью TDictionary, например:

interface

type
  MyBase = class
  public
    constructor Create; virtual;
  end;

  MyBaseClass = class of MyBase;

  MyClass1 = class(MyBase)
  public
    constructor Create; override;
  end;

  MyClass2 = class(MyBase)
  public
    constructor Create; override;
  end;

  MyClass3 = class(MyBase)
  public
    constructor Create; override;
  end;

...

function MakeMyClass(const Name: string): MyBase;

implementation

uses
  System.Generics.Collections, System.SysUtils;

var
  MyFactory: TDictionary<string, MyBaseClass>;

function MakeMyClass(const Name: string): MyBase;
begin
  Result := MyFactory[Name].Create;
end;

...

initialization
  MyFactory := TDictionary<string, MyBaseClass>.Create;
  MyFactory.Add('xxx1', MyClass1);
  MyFactory.Add('xxx2', MyClass2);
  MyFactory.Add('xxx3', MyClass3);
finalization
  MyFactory.Free;
end.

Тогда вы можете сделать это:

var
  MyObj: MyBase;
begin
  MyObj := MakeMyClass('xxx1');
  ...
  MyObj.Free;
end;

Разве TMyBaseCreateFunc не должен быть вместо MyBaseClass?? Спасибо за вашу помощь.

TomB 17.06.2021 00:17

@TomB Да, так и должно быть. Это остатки другой версии кода, где я использовал указатели на функции, создающие экземпляры объектов. Я исправил это в своем примере здесь, спасибо.

Remy Lebeau 17.06.2021 00:55

Другие вопросы по теме