【问题标题】:How can I create a function with an arbitrary number of parameters?如何创建具有任意数量参数的函数?
【发布时间】:2011-11-29 01:06:17
【问题描述】:

我想创建一个接收多个字符串作为参数的函数。 就像 C 的函数 printf("Hello %s",name); 一样。但我不想传递一个准备好的数组,它不可读。

Edit1.text:=lang('Hello');

Edit2.text:=lang('Welcome to {1} guest',place);

Edit3.text:=lang('Hi {1}, is your {2} time in {3}','Victor','first','Disney');

输出应该是:

Hello
Welcome to Disney guest
Hi Victor is your first time in Disney

我是如何创建function TForm1.lang(parameters:String):String; 的,我进行了研究,但我无法让它发挥作用。

我还需要访问 parameters[] 和 parameters.length。

我需要这个来将我的应用程序转换为多语言。

【问题讨论】:

  • How can a function with 'varargs' retrieve the contents of the stack? 的可能重复项(请参阅已接受的答案)
  • 您知道 Delphi 内置了对国际化的支持吗?它可能不是绝对最好的,但它肯定比从头开始自己滚动要好。请参阅帮助文件,并搜索“国际应用程序”以获取有关使用它的教程。

标签: delphi function parameters delphi-7


【解决方案1】:

这是一个示例函数,说明如何做到这一点:

function TForm1.lang(s: String; params: array of String): String;
var
  i: Integer;
begin
  for i := 0 to High(params) do
  begin
    ShowMessage(params[i]);
  end;
end;

这样称呼它:

lang('My format string', ['this', 'that']);

或者像这样:

var
  b: String;
begin
  b := 'this';
  lang('My format string', [b, 'that']);
end;

【讨论】:

  • 这对我来说似乎没问题。但是我可以让第二个参数可选吗?
  • 问:你的意思是“我可以消除传递数组”吗?答:没有。因为上面提到的“Pascal 调用约定”。
  • Vitimtk,你可以传递任意数量的数组元素。使用[] 传入一个空数组。使用空数组,High(params) = -1
  • PS:Delphi 列表很简单; Delphi 变量数组很简单。两者都是可读的。如果有疑问,请将它们与 C++ STL 容器进行比较;)
  • 我想我只需要像@TonyHopkinson 回答这样的数组参数。谢谢!
【解决方案2】:

不知道你说的不可读是什么意思

DoSomething(['Param1','Param2']);

procedure DoSomething(args : Array of String);
Var
  Index : Integer;
Begin
  for index := Low(args) to High(args) Do
    ShowMessage(args[Index]);
End;

我觉得还可以。当然,如果您想从 delphi 外部调用它,那么您就有问题了。

快速修复只是传入一个分隔字符串,然后用户 TStringList 将其拆分。

您可以编写一个小函数来执行此操作,完成后不要忘记释放它。

【讨论】:

    【解决方案3】:

    您的所有三个示例都可以使用 SysUtils.Format 修复:

    Edit1.text := format('%s',['Hello']));
    Edit1.text := format('Welcome to %s guest',[place]));
    Edit1.text := format('Hi %s, is your %s time in %s',['Victor','first','Disney']));
    

    我个人认为它的可读性很强。如果您可以从基本的 sysutils 函数中获得所需的东西,那么您应该认真考虑这样做,而不是编写自己的版本。另一方面,您可能需要问题中未显示的更复杂的功能。如果是这样的话,我认为 paulsm4 使用字符串列表的建议似乎是一个不错的选择。

    【讨论】:

    • 我不知道这个功能,但是我想自己写,因为我不只是将文本替换为相应的%s,还要选择正确的语言,lang('Welcome {1}','Svein')可以返回Ben-venido Svein 如果您的系统是西班牙语。不知道你有没有...
    【解决方案4】:

    Delphi 不支持使用vararg 样式参数创建函数,这些参数的工作方式与printf() 完全相同。它仅支持从外部库中使用此类功能。最接近的 Delphi 支持使用可变参数列表创建函数是使用“开放数组”参数,就像 SysUtils.Format() 使用的那样。

    【讨论】:

    • Variant打开数组,其实就是网上手册题目的名字
    【解决方案5】:

    正如 Tony 上面提到的,我还建议使用分隔字符串。除了,不仅仅是分隔,而是使用更多的解析技术。如果我理解正确,您为格式化所做的这个函数不应在参数中包含数组,但从技术上讲,这并不意味着我们根本不能在任何地方使用数组(数组非常适合用于这种情况速度快)。

    此方法将允许在参数中传递几乎任何内容,包括分隔符,而不会影响输出。这个想法是做A)参数字符串的大小,B)大小和参数之间的分隔符,C)参数字符串......并重复......

    const
      MY_DELIM = '|';  //Define a deliminator
    
    type
      TStringArray = array of String;
    
    /////////////////////////////////
    
    //Convert an array of string to a single parsable string
    //  (Will be the first step before calling your format function)
    function MakeParams(const Params: array of String): String;
    var
      X: Integer;
      S: String;
    begin
      Result:= '';
      for X:= 0 to Length(Params)-1 do begin
        S:= Params[X];
        Result:= Result + IntToStr(Length(S)) + MY_DELIM + S;
      end;
    end;
    
    //Convert a single parsable string to an array of string
    //  (Will be called inside your format function to decode)
    //  This is more or less called parsing
    function ExtractParams(const Params: String): TStringArray;
    var
      S: String;  //Used for temporary parsing
      T: String;  //Used for copying temporary data from string
      P: Integer; //Used for finding positions
      C: Integer; //Used for keeping track of param count
      Z: Integer; //Used for keeping track of parameter sizes
    begin
      S:= Params;                   //Because we'll be using 'Delete' command
      C:= 0;                        //Set count to 0 to start
      SetLength(Result, 0);         //Prepare result array to 0 parameters
      while Length(S) > 0 do begin  //Do loop until nothing's left
        P:= Pos(MY_DELIM, S);       //Get position of next deliminator
        if P > 1 then begin         //If deliminator was found...       
          C:= C + 1;                  //We have a new parameter
          SetLength(Result, C);       //Set array length to new parameter count
          T:= Copy(S, 1, P-1);        //Get all text up to where deliminator was found
          Delete(S, 1, P);            //Delete what we just copied, including deliminator
          Z:= StrToIntDef(T, 0);      //Convert T:String to Z: Integer for size of parameter
          T:= Copy(S, 1, Z);          //Get all text up to 'Z' (size of parameter)
          Delete(S, 1, Z);            //Delete what we just copied
          Result[C-1]:= T;              //Assign the new parameter to end of array result
        end else begin              //If deliminator was NOT found...
          S:= '';                     //Clear S to exit loop (possible bad format if this happens)
        end;
      end;
    end;
    
    //Main formatting routine
    function MyFormat(const Input: String; const Params: String): String;
    var
      A: TStringArray;
      X: Integer;
      S: String;
      P: Integer;
      R: String;
    begin
      R:= Input;
      A:= ExtractParams(Params);
      //At this point, A contains all the parameters parsed from 'Params'
      for X:= 0 to Length(A)-1 do begin
        S:= A[X];
        P:= Pos('%s', R);
        if P > 0 then begin
          Delete(R, P, 2);
          Insert(S, R, P);
        end;
      end;
      Result:= R;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      Pars: String;
    begin
      Pars:= MakeParams(['this', 'that', 'something else']);
      Edit1.Text:= MyFormat('%s is %s but not %s', Pars);
    end;
    

    【讨论】:

    • 我在设计自定义服务器/客户端数据包系统时使用了这种方法。我从原始的 TServerSocket 和 TClientSocket 开始并围绕它们进行构建 - 使用上述技术,通过这些套接字发送/接收命令变得容易 20 倍。方法还可以重复用于许多其他事情,例如,如果要将字符串数组保存到文本文件中。
    • 不知道你为什么得到它们。对我来说,这似乎是一个值得信赖的开始。
    【解决方案6】:

    您可能知道,SysUtils.Format() 通过使用集合来实现“可变参数”。

    但是,在您的情况下,为什么不只传递一个 TStringList 呢?该函数将简单地检查“list.Count”。瞧——你完成了!

    【讨论】:

    • PS:C 可变参数以它们的方式工作的原因是,在“C 调用约定”中,CALLER 会清理堆栈。 CALLER 知道在这里弹出 5 个变量,在那里弹出 10 个变量。 Delphi 使用“Pascal 调用约定”,其中 CALLEE 清理堆栈。这稍微更有效,但这意味着子例程必须接受固定的#/arguments。所以你只剩下传递某种“列表”(一个数组、一个列表、一个集合——随便什么)。但“一件事”为“一个论点”。 '希望对您有所帮助......
    • 因为我需要声明一个变量实例TStringList,然后作为参数传递......到目前为止这并不容易,甚至不可读。我只是 delphi 的新手。
    • 我不知道如何实现“可变参数”
    • “SysUtils.Format() 通过使用集合实现“varargs””实际上是不正确的
    • @paulsm4:听上去,这个函数会产生一些繁重的活动,如果这是真的,那么 TStringList 就太重了。
    猜你喜欢
    • 2014-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-14
    • 2015-04-15
    • 1970-01-01
    • 2011-05-09
    相关资源
    最近更新 更多