«Именованные параметры» в Delphi

Иногда возникает ситуация, когда в функцию требуется передавать много различных параметров, но при этом необходимый набор этих параметров может сильно различаться. В таких случаях, для Delphi, как правило, есть несколько путей решения:
  1. Просто забить все возможные параметры в интерфейс функции.
  2. Сделать множество перегрузок функции на все случаи жизни.
  3. Передать параметры массивом.
  4. Воспользоваться обходным путём. Например, вынести параметры в класс и проставлять их перед вызовом функции.
Всё эти способы получаются довольно громоздкими в реализации и имеют массу недостатков. А самое главное, что над их реализацией необходимо думать в каждом конкретном случае отдельно — не существует простого общего решения.

В некоторых языках (Scala, Python, Ruby...) такой проблемы не стоит в принципе — там есть такая замечательная вещь как именованные параметры. В Delphi же приходится всегда следовать установленному порядку аргументов. Не спасают даже значения по-умолчанию (их не всегда можно применить из-за конфликта типов, к тому же их использование нередко приводит к путанице).

Однако, используя небольшую хитрость, в Delphi вполне можно написать, к примеру, вот так:

ProcessParams(Par('Param1', 'test') + Par('Param2', 38) + Par('Param3', 3.2));

При этом в функцию ProcessParams придёт массив из трёх записей, содержащих пару «имя — значение». Такая запись становится возможной благодаря модулю объёмом всего 40 строк:

unit NamedParams;

interface

type

TNamedParameter = record
 Name: string;
 Value: Variant;
 end;

TNamedParameters = record
 Parameters: array of TNamedParameter;
 procedure AddParam(const ParamName: string; ParamValue: Variant);
 constructor Create(const ParamName: string; ParamValue: Variant);
 class operator Add(Parameter1, Parameter2: TNamedParameters): TNamedParameters;
 end;

function Par(const ParamName: string; ParamValue: Variant): TNamedParameters;

implementation

function Par(const ParamName: string; ParamValue: Variant): TNamedParameters;
begin
 Result := TNamedParameters.Create(ParamName, ParamValue);
end;

class operator TNamedParameters.Add(Parameter1, Parameter2: TNamedParameters): TNamedParameters;
var
 TempParam: TNamedParameter;
begin
 for TempParam in Parameter1.Parameters do
 Result.AddParam(TempParam.Name, TempParam.Value);
 for TempParam in Parameter2.Parameters do
 Result.AddParam(TempParam.Name, TempParam.Value);
end;

procedure TNamedParameters.AddParam(const ParamName: string; ParamValue: Variant);
begin
 SetLength(Parameters, Length(Parameters) + 1);
 Parameters[ High(Parameters)].Name := ParamName;
 Parameters[ High(Parameters)].Value := ParamValue;
end;

constructor TNamedParameters.Create(const ParamName: string; ParamValue: Variant);
begin
 AddParam(ParamName, ParamValue);
end;

end.


Немного расскажу о реализации. Функция Par в данном случае служит лишь обёрткой для конструктора объекта параметров. Благодаря ей, мы можем сделать использование метода максимально ёмким и простым. Сам же объект выполняет роль накопителя свойств, которые можно складывать между собой, благодаря перекрытию оператора сложения для самого объекта. Тип record выбран тоже не просто так. Во-первых, перекрытие операторов для классов работает только в .Net, а во-вторых, при такой реализации мы можем не напрягаться с освобождением памяти.

Использование этого метода передачи параметров в функции также предельно просто:

procedure ProcessParams(Params: TNamedParameters);
const
 ParamStr = '%s = %s'#13#10;
var
 TempParam: TNamedParameter;
 Str: string;
begin
 Str := '';
 for TempParam in Params.Parameters do
 begin
 if VarIsFloat(TempParam.Value) then
 Str := Str + Format(ParamStr, [TempParam.Name, FloatToStr(TempParam.Value)])
 else if VarIsNumeric(TempParam.Value) then
 Str := Str + Format(ParamStr, [TempParam.Name, IntToStr(TempParam.Value)])
 else
 Str := Str + Format(ParamStr, [TempParam.Name, TempParam.Value]);
 end;
 ShowMessage(Str);
end;


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


0 комментариев

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.