Как правильно готовить TDictionary<string, T>

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Как правильно готовить TDictionary<string, T>

Сообщение GreyCrazyWolf » 16.06.2025 23:19:39

Добрейшего времени суток.
Понадобилось в проекте использовать
Код: Выделить всё
TDictionary<string, T>

в итоге то что я смог найти выродилось вот в такое
Код: Выделить всё
// lasarusовский генерек словарь
  AdapterTypesClass = class of TBaseAdapter; // некий базовый класс, потомков которого надо хранить в этом листе
  AdapterTypesDict = specialize TDictionary<string, AdapterTypesClass>;  // сам лист сопоставление имен и классов

используется для создания нужного экземпляра класса
Код: Выделить всё
// Интерфейс фабрики для создания экземпляров
  IAdapterFactory = interface
    function CreateAdapter(AOfficeNum: Integer; AOpType: TOperType; AAdapterName: string): TBaseAdapter;
  end;

  // Фабрика для создания экземпляров TBaseAdapter
  TAdapterFactory = class(TInterfacedObject, IAdapterFactory)
  private
    FAdapterTypes: AdapterTypesDict;
  public
    constructor Create;
    destructor Destroy; override;
    procedure RegisterAdapterType(AName: string; AClass: AdapterTypesClass);
    function CreateAdapter(AOfficeNum: Integer; AOpType: TOperType; AAdapterName: string): TBaseAdapter;
    procedure FillAdapters;
  end;       

constructor TAdapterFactory.Create;
begin
  FAdapterTypes := AdapterTypesDict.Create();
end;

destructor TAdapterFactory.Destroy;
begin
  FAdapterTypes.Free;
  inherited;
end;

procedure TAdapterFactory.RegisterAdapterType(AName: string; AClass: AdapterTypesClass);
begin
  FAdapterTypes.Add(AName, AClass);
end;

function TAdapterFactory.CreateAdapter(AOfficeNum: Integer; AOpType: TOperType; AAdapterName: string): TBaseAdapter;
var
  AdapterClass: AdapterTypesClass;
begin
  if not FAdapterTypes.TryGetValue(AAdapterName, AdapterClass) then
    raise Exception.Create('Adapter type not registered: ' + AAdapterName);

  Result := AdapterClass.Create(AOfficeNum, AOpType);
end;

procedure TAdapterFactory.FillAdapters;
begin
  RegisterAdapterType('LoginChangeAdapter', TLoginChangeAdapter);
  RegisterAdapterType('FindUserAdapter' , TFindUserAdapter);
  RegisterAdapterType('GetUserAdapter', TGetUserAdapter);
  RegisterAdapterType('GetGroupAdapter' , TGetGroupAdapter);
  RegisterAdapterType('UserStateAdapter ', TUserStateAdapter );
  RegisterAdapterType('PassChangeAdapter ' , TPassChangeAdapter );
end;                     

Извиняюсь за количество кода, сам уже малость запутался
Собсвенно вопросы
1. В куче мест написано что
Код: Выделить всё
FAdapterTypes := AdapterTypesDict.Create();

нужно вызывать с опцией [doOwnsValues], но у меня такого конструктора нет
Код: Выделить всё
использую под astroй Lazarus 3.2 (rev Unversioned directory) FPC 3.2.2 x86_64-linux-gtk2

2. Есть ли более простой способ реализации аналога C#
Код: Выделить всё
public class Foo<T> : Form where T : class, new()
{
public int Boo<T> (int id)
{
  T someT = new T();
  someT.somemethod(id);
}
}
GreyCrazyWolf
новенький
 
Сообщения: 12
Зарегистрирован: 02.03.2023 15:23:57

Re: Как правильно готовить TDictionary<string, T>

Сообщение iskander » 17.06.2025 00:53:40

GreyCrazyWolf писал(а):1. В куче мест написано что
Код: Выделить всё
FAdapterTypes := AdapterTypesDict.Create();

нужно вызывать с опцией [doOwnsValues], но у меня такого конструктора нет

Заменить TDictionary на TObjectDictionary.

GreyCrazyWolf писал(а):2. Есть ли более простой способ реализации аналога C#
Код: Выделить всё
...

Graecum est; non potest legi :)
Генерик с ограничением параметров?
iskander
энтузиаст
 
Сообщения: 623
Зарегистрирован: 08.01.2012 18:43:34

Re: Как правильно готовить TDictionary<string, T>

Сообщение GreyCrazyWolf » 17.06.2025 02:31:59

iskander писал(а):Генерик с ограничением параметров?

Ну, в целом нужно чего, может я вообще не туда копаю, просто интуитивно делаю как сделал бы на шарпе :?
Приложение представляет нечто вроде REST интерфейса, в котором в зависимости от урла будет исполнятся тот или иной адаптер
Адаптеры представляют из себя класс основанный на
Код: Выделить всё
// базовый класс для адаптеров
  TBaseAdapter = class(TPersistent)  // класс для структуры JSON
  private
    fSearchField : string;           // базовые поля запроса - тип
    fSearchValue : string;           // базовые поля запроса - поле для поиска
    fOfficeNum   : integer;          // офис
    fMessage     : string;           // сообщение
    fOpType      : TOperType;        // тип операции
  public
    constructor Create(AOfficeNum: Integer; AOpType: TOperType); virtual;
    destructor Destroy; override;
    function FillData(AJSONObject: TJSONObject): TOpResult; virtual; abstract;
    function Execute: TOpResult; virtual; abstract;
    function GetMessage: string;
  published                          // все свойства должны быть published
    property searchField: string read fSearchField write fSearchField;
    property searchValue: string read fSearchValue write fSearchValue;
  end;       

у класса обязательны два метода
Код: Выделить всё
function FillData(AJSONObject: TJSONObject): TOpResult; virtual; abstract;

отвечает за маппинг джосна, который передается в POST запросе на поля класса, проверку данных, если он вернул все хорошо, то выполняется
Код: Выделить всё
function Execute: TOpResult; virtual; abstract;

который реализует логику
например мы по урлу
Код: Выделить всё
http://localhost/finduser

передали джосн с условиями поиска пользователей, он должен вызвать адаптер FindUserAdapter' , TFindUserAdapter, который вернет список найденных пользователей
В самом приложении роутингом связаны маршруты и обработчики
Код: Выделить всё
FRouter.RegisterRoute('/service/findUser', @findEmploeeEndpoint );
FRouter.RegisterRoute('/service/getUserData', @getEmploeeEndpoint ); 

и хотелось чтоб было как-то так
Код: Выделить всё
// 10.2. Считывание списка существующих УЗП – findUser
procedure TSrv.findEmploeeEndpoint(ARequest: TRequest; AResponse: TResponse);
var
  JsonData         : TJSONData;
  ResultContent : string;
  ResultCode     : integer;
  opResult         : TOpResult;
  ResultRec       :TAdapterResultRec
begin
     JsonData        := GetJSON(ARequest.Content);
     ResultRec := AdapterGetResult('FindUser', JsonData as TJSONObject, 1, AOpType);

........
end; 

function TSrv.AdapterGetResult(AAdapterName: string; AJson: TJSONObject; AOfficeNum: Integer; AOpType: TOperType): TAdapterResultRec;
var
  Adapter   : TBaseAdapter;
  opResult  : TOpResult;
begin
  Result.Code := 0;
  Result.Content := '';
  Adapter := nil;

  try
    Adapter := FAdapterFactory.CreateAdapter(AOfficeNum, AOpType, AAdapterName);
    opResult := Adapter.FillData(AJson);
    // ... process opResult

  except
    on E : Exception do
    begin
      Result.Code     := 500;
      Result.Content  := 'Ошибка сервера: ' + E.ToString;
    end;
  finally
    if Assigned(Adapter) then
      FreeAndNil(Adapter);
  end;
end;       
GreyCrazyWolf
новенький
 
Сообщения: 12
Зарегистрирован: 02.03.2023 15:23:57

Re: Как правильно готовить TDictionary<string, T>

Сообщение iskander » 17.06.2025 08:37:45

GreyCrazyWolf писал(а):и хотелось чтоб было как-то так

Ну и хорошо, а что этому мешает?
iskander
энтузиаст
 
Сообщения: 623
Зарегистрирован: 08.01.2012 18:43:34

Re: Как правильно готовить TDictionary<string, T>

Сообщение GreyCrazyWolf » 20.06.2025 22:35:19

iskander писал(а):Ну и хорошо, а что этому мешает?

Извиняюсь за долгий ответ.
Да собственно ничего не мешает, оно даже работает, спасибо.
Мне просто, как человеку начавшему писать на Лазарусе недавно, просто интересно насколько это правильно реализовано, все таки после чего-то подобного
Код: Выделить всё
public void InvoiceRegistration<T>(List<T> list) where T : BaseInvoice
{

}

выглядит несколько монструозно :shock:
GreyCrazyWolf
новенький
 
Сообщения: 12
Зарегистрирован: 02.03.2023 15:23:57

Re: Как правильно готовить TDictionary<string, T>

Сообщение iskander » 21.06.2025 14:16:25

GreyCrazyWolf писал(а):как человеку начавшему писать на Лазарусе недавно

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

GreyCrazyWolf писал(а):...все таки после чего-то подобного
Код: Выделить всё
public void InvoiceRegistration<T>(List<T> list) where T : BaseInvoice
{

}

выглядит несколько монструозно

Режим совместимости {$mode delphi} несколько менее многословен.
Имхо прямая калька могла бы выглядеть как-то так:
Код: Выделить всё
  procedure InvoiceRegistration<T: TBaseInvoice>(List: TList<T>);
iskander
энтузиаст
 
Сообщения: 623
Зарегистрирован: 08.01.2012 18:43:34


Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 236

Рейтинг@Mail.ru