Pull to refresh

«Именованные параметры» в 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;


Пример вызова данной функции был описан в начале статьи.
Как видите, этот метод позволяет легко и не задумываясь, а главное наглядно, передавать в функцию любой массив параметров одним выражением.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.