【问题标题】:How to create a function which accepts variable number of variable arguments?如何创建一个接受可变数量可变参数的函数?
【发布时间】:2012-11-07 12:45:37
【问题描述】:

可以创建一个接受可变数量参数的函数:

function f(const x: array of const): String;

并以这种方式使用它:

f([1,3,4, "hello"]);

也可以将参数定义为“可变的”:

function g(var x: Byte): String;

var B: Byte;
g(B);

但是是否有可能定义一个函数,它可以接受任意数量的任何类型的参数并更改它们的所有值? 我知道我可以使用指针来做到这一点,但是我不知道传递的参数的类型,所以弄乱它们是非常不安全的。


我只想创建一个函数,它可以返回多种不同类型的变量,而不仅仅是一种类型或只有 1 个变量。而且我不想写无数行来使用这个函数——它应该只是函数本身,在函数调用之前没有 SetLength() 或任何东西。所以这是迄今为止我做的最好的东西:

type TVarArr = Array of Variant;
     PVarArr = ^TVarArr;
Procedure f(a: PVarArr);
var
 i:Integer;
begin
  SetLength(A^, 4);
  a^[0] := 46;
end;

ar: TVarArr;
begin
f(@ar);
caption := IntToStr(ar[0]);

【问题讨论】:

  • function f(var x: array of const): String; 如此处所述:docwiki.embarcadero.com/RADStudio/XE3/en/…
  • @DavidHeffernan 但我不能这样调用它:f([a, b, c]) 因为它给了我一个错误:“常量对象不能作为 var 参数传递”。那么如何调用这个函数呢?
  • 好吧,我错了。你不能做你想做的事。对不起
  • 不可能这样做,因为该函数没有收到任何要更改的内容。在f([1, 3, 4, 'hello'])的情况下,接收的是值1、2、3和常量字符串hello。这就是为什么它被称为array of const - 因为它是恒定的(不可更改的)。
  • 这是一个开放数组。使用动态数组:var a: TVarArray。从您的 cmets 中可以清楚地看出,您需要修改您对开放数组的了解。

标签: delphi


【解决方案1】:

Tom 将无法使用此答案,因为他的 Delphi 版本不够高,但 D2010 或更高版本的任何人都可以在此类挑战中充分利用扩展 rtti 的 TValue。

以下是一个小型控制台应用程序,展示了如何:

  • 从一个开放的数组参数创建一个动态的 TValue 数组;
  • 将 TValue 的动态数组复制到一个新的动态数组,在途中修改各个值;
  • “就地”修改动态 TValue 数组中的项目。

享受吧。

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.Rtti,
  System.SysUtils,
  System.TypInfo;

const
  StringKinds: set of TTypeKind = [tkChar, tkString, tkWChar, tkLString, tkWString, tkUString];

type
  TValueArray = array of TValue;

function ValueArrayFromConstArray(const aSource: array of TValue): TValueArray;
var
  idx: Integer;
begin
  SetLength(Result, Length(aSource));
  for idx := Low(aSource) to High(aSource) do
    Result[idx] := aSource[idx];
end;

function ReturnNewArray(const aSource: TValueArray): TValueArray;
var
  idx: Integer;
begin
  SetLength(Result, Length(aSource));
  for idx := Low(aSource) to High(aSource) do
    if aSource[idx].Kind in StringKinds then
      Result[idx] := 'Dest' + aSource[idx].ToString
    else
      if aSource[idx].Kind in [tkInteger] then
        Result[idx] := 10 + aSource[idx].AsInteger
      else
        Result[idx] := aSource[idx];
end;

procedure ModifyArrayValues(var aArray: TValueArray);
var
  idx: Integer;
begin
  for idx := Low(aArray) to High(aArray) do
    if aArray[idx].Kind in StringKinds then
      aArray[idx] := 'Dest' + aArray[idx].ToString
    else
      if aArray[idx].Kind in [tkInteger] then
        aArray[idx] := 10 + aArray[idx].AsInteger
      else
        ;//aArray[idx] := aArray[idx];
end;

var
  Source: TValueArray;
  Destination: TValueArray;
  Item: TValue;
  idx: Integer;
begin
  Source := ValueArrayFromConstArray(['Some', 42, TObject]);
  Destination := ReturnNewArray(Source);
  idx := 0;
  WriteLn('', #9, 'Old', #9, 'New');
  WriteLn('-', #9, '----', #9, '----');
  for Item in Source do
  begin
    WriteLn(idx, #9, Item.ToString, #9, Destination[idx].ToString);
    Inc(idx);
  end;
  WriteLn;
  WriteLn;
  WriteLn('', #9, 'Modified');
  WriteLn('-', #9, '----');
  Source := ValueArrayFromConstArray(['first', 50, TValue.From<TFloatValue>(fvCurrency)]);
  ModifyArrayValues(Source);
  for Item in Source do
  begin
    WriteLn(idx, #9, Item.ToString);
  end;
  ReadLn;
end.

【讨论】:

  • 您正在创建原始数组的副本并修改该副本,而不是修改原始数组(这是问题所问的),AFAICT。这些似乎是两个不同的东西。
  • @KenWhite 在 ReturnNewArray 我是,在 ModifyArrayValues 我不是。
  • 当然可以。您使用Source := ValueArrayFromConstArray 创建数组(这与使用ModifyArrayValues([varA, varB, varC]); 传递array of const 然后将copy (Source) 传递给ModifyArrayValues 不同;这不一样就像修改原始数组一样。
  • @KenWhite:哦,这就是你所说的部分。是的,我正在创建一个新数组。这只是一种获取包含各种不同类型项目的数组的方法,可以轻松初始化(即无需执行 SetLength)。我确信可以“改进”做一些就地魔术,因为开放数组与动态数组兼容。但为什么?我不认为这是问题中的要求。我读了 OP 有点神秘的问题和各种 cmets,只是希望能够返回一个包含不同类型项目的数组,或者在传递给函数时修改一个项目。
  • 您可以使用array of Variant 来做到这一点。问题是关于更改创建为func([a, b, c]) 的数组的值,其中abc 的类型不同(请参阅原始问题中的第一个代码sn-p)。 array of Variantarray of const 之间是有区别的——我相信你知道 :-)。我不知道您是否可以看到汤姆删除的“答案”(这是作为答案发布的扩展评论);我不记得那个副手需要什么代表,但它也提到了这一点。
【解决方案2】:
Procedure All(var a:Array of Variant);
var
 i:Integer;
begin
  for I := Low(a) to High(a) do
      begin

        if VarType(a[i])=258  then
          a[i] := a[i] + ' modified';

      end;
end;

Procedure AllConst( a:Array of Variant);
var
 i:Integer;
begin
  for I := Low(a) to High(a) do
      begin
        Showmessage(a[i]);
      end;
end;


procedure TForm3.Button1Click(Sender: TObject);
var
 a:Array of Variant;
begin
  AllConst([1,2,'Test']);
  SetLength(a,3);
  a[0] := 3.141;
  a[1] := 'Test';
  a[2] := 27;
  all(a);
  Showmessage(a[1]);
end;

【讨论】:

  • 这个问题是 AllConst 接受可变数量的参数,但不能更改它们,而 All() 可以更改参数但只接受其中的 1 个。
  • @David Heffermann,没错,我从来不用写我自己的变体类型...uweraabe.de/Blog/2010/08/07/a-magical-gathering-part-2
  • 这样的事情怎么样:Procedure All(out a:Array of Variant);开始设置长度(A,4); //这不会编译,但如果它编译,那将是一个很好的解决方案结束;
  • 这将编译 .... type TVariantArray=Array of Variant; {$R *.dfm} 程序 All(out a:TVariantArray);开始 SetLength(a,10); a[1] := '测试';结尾;过程 TForm3.Button1Click(Sender: TObject); var a:TVariantArray;开始所有(一); Showmessage(a[1]);结束;
猜你喜欢
  • 2011-11-28
  • 2011-08-08
  • 1970-01-01
  • 2018-02-02
  • 1970-01-01
  • 2012-12-25
  • 2011-04-01
相关资源
最近更新 更多