«Именованные параметры» в Delphi
Иногда возникает ситуация, когда в функцию требуется передавать много различных параметров, но при этом необходимый набор этих параметров может сильно различаться. В таких случаях, для Delphi, как правило, есть несколько путей решения:
В некоторых языках (Scala, Python, Ruby...) такой проблемы не стоит в принципе — там есть такая замечательная вещь как именованные параметры. В Delphi же приходится всегда следовать установленному порядку аргументов. Не спасают даже значения по-умолчанию (их не всегда можно применить из-за конфликта типов, к тому же их использование нередко приводит к путанице).
Однако, используя небольшую хитрость, в Delphi вполне можно написать, к примеру, вот так:
При этом в функцию ProcessParams придёт массив из трёх записей, содержащих пару «имя — значение». Такая запись становится возможной благодаря модулю объёмом всего 40 строк:
Немного расскажу о реализации. Функция Par в данном случае служит лишь обёрткой для конструктора объекта параметров. Благодаря ей, мы можем сделать использование метода максимально ёмким и простым. Сам же объект выполняет роль накопителя свойств, которые можно складывать между собой, благодаря перекрытию оператора сложения для самого объекта. Тип record выбран тоже не просто так. Во-первых, перекрытие операторов для классов работает только в .Net, а во-вторых, при такой реализации мы можем не напрягаться с освобождением памяти.
Использование этого метода передачи параметров в функции также предельно просто:
Пример вызова данной функции был описан в начале статьи.
Как видите, этот метод позволяет легко и не задумываясь, а главное наглядно, передавать в функцию любой массив параметров одним выражением.
- Просто забить все возможные параметры в интерфейс функции.
- Сделать множество перегрузок функции на все случаи жизни.
- Передать параметры массивом.
- Воспользоваться обходным путём. Например, вынести параметры в класс и проставлять их перед вызовом функции.
В некоторых языках (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 комментариев