Delphi: Одна процедура для изменения всех полей записи?

#delphi #record #procedure

#delphi #запись #процедура

Вопрос:

У меня есть запись, подобная этой:

 Tcustomer=record
  Name: string;
  IDNumber: Integer;
  IsMarried: boolean;
end;
  

И у меня есть TCustomers_Manager класс, который хранит список всех клиентов. Возможно ли иметь процедуру, подобную этой:

 Procedure ChangeCustomer(CustomerIndex: integer; field: string; value);
  

Это устанавливает значение для этого конкретного поля. Например:

 ChangeCustomer(1, 'Name','John');
  

Как я могу это реализовать?

Обновление: Чтобы уточнить, мой вопрос в основном состоит из 2 частей:

1) Как я могу сопоставить имя поля (в строке) с фактическим полем в записи?

2) Возможно ли передать значение, имеющее разные типы? Или я должен передать один тип и привести его к типу (например, передать строку, а затем использовать strtoint() )

Комментарии:

1. Да, вы можете сделать это в самых последних версиях Delphi, используя информацию о типе во время выполнения. Однако не всегда отказываться от системы статических типов — такая уж отличная идея. Вы уверены, что вам нужно это сделать?

2. Какое «конкретное поле» вы имеете в виду? Вы должны определить своих заказчиков. Вы используете IDNumber для этого? Является ли этот IDNumber уникальным? ChangeCustomer должен знать, какого клиента менять. Вам также нужен «AddCustomer». Что вы имеете в виду под «Списком клиентов»? TList или массив? Я бы использовал небольшую базу данных (возможно, в памяти) для выполнения вашей задачи.

3. @Andreas: Пожалуйста… Это всего лишь часть кода, по которой у меня возник вопрос, а не весь проект!! У меня есть TList<TCustomer> в моем классе. Я передам индекс клиента в ChangeCustomer. У меня, очевидно, есть процедура AddCustomer (). И нет, это слишком просто, чтобы использовать для этого базу данных.

Ответ №1:

Я бы использовал TClientDataSet.

  1. Создайте TClientDataSet с идентификатором полей, именем и т.д.
  2. Откройте Dataset, заполните с помощью InsertRecord или Insert/Post
  3. Найти запись с помощью Locate
  4. Используйте FieldByName(‘имя_поля’).Значение для доступа к данным или их изменения

Или вы можете взять любой компонент MemoryDataSet и использовать его таким же образом.

Второй способ — преобразовать запись в класс, объявить поля как опубликованные и использовать SetPropValue .

Если вы хотите передать какое-либо значение, вы можете использовать Variant. Но вы должны проверять типы перед назначением.

Комментарии:

1. Второй способ — это именно то, что я искал. ‘Variant’ для значения и ‘SetPropValue’ для изменения значений свойств с использованием их имен. Большое спасибо.

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

Ответ №2:

Вы могли бы, например, сделать это (предполагая lst: TList<TCustomer> , что вы сказали в комментарии):

 Procedure ChangeCustomer(index: integer; i: byte; value: variant)
begin
    case (i) of
        0: lst[index].Name := value;
        1: lst[index].IDNumber := value;
        2: lst[index].IsMarried := value;
    end;
end;
  

Вы могли бы использовать тип (или перечисление) вместо i: byte .
Я давно не использую Delphi, поэтому воспринимайте мой пример как идею, а не как приложение Delphi!!

Комментарии:

1. Почему понижающий голос? Оставьте комментарий с объяснением, чтобы я мог понять и исправить при необходимости, пожалуйста!!!

2. Идея хорошая, но как ChangeCustomer узнает, какую запись нужно изменить? Вся концепция является неполной…

3. Это плохая идея. По двум причинам: 1) Это может сработать для записи с 3 полями, но как насчет 30 полей? Это будет беспорядок. 2) Тип значения отличается для каждого поля. Какой тип значения в определении вашей процедуры (в аргументах)?

4. @flom вам также приходится иметь дело с проблемой типа значения. Каков ваш план?

5. @Flom, SetPropValue работает только для классов, не для записей. Это делается с использованием RTTI. Вы можете написать свой собственный вариант, который также работает с записями, поскольку Delphi 2010 RTTI также генерируется для записей (вы упомянули, что используете TList<> , значит, вы на Delphi 2010 )