为什么我不能返回字符串的任意阵列?(Why can't I return arbitrary

2019-08-21 06:22发布

编译器可以让我做到以下几点:

procedure MyProc(const ADynData: array of string);

要么

procedure MyProc(const ADynData: TStringDynArray);

并通过像这样任意的数据:

MyProc(['Data1', 'Data2']);

然而,将不允许

function MyFunc: TStringDynArray;
....
function MyFunc: TStringDynArray;
begin
    Result := ['Data1', 'Data2'];
end;

要么

function MyFunc: TStringDynArray;
const
    CDynData: array[0..1] of string = ('Data1', 'Data2');
begin
    Result := CDynData;
end;

为什么是这样? 这不是技术上是一回事吗?

对于这些特殊的情况是推荐什么(最有效的)返回字符串的任意阵列的方法是什么?

Answer 1:

不,这不是一回事。 在

procedure MyProc(const ADynData: array of string);

参数是一个开放数组参数 ,这是不一样的东西作为一个“普通” 动态数组 。 的[..]的语法仅可用于创建在功能开放数组参数开放阵列。 (否则, [..]用于在代码以指定 ,如Font.Style := [fsBold, fsItalic]但是, 集只能有有序类型作为其“基本类型”,因此仍然没有这样的东西“设置字符串”。)

换句话说,它是不可能写在动态数组中的代码就像你在你的第二个代码片段尝试,

function MyFunc: TStringDynArray;
begin
  result := ['Data1', 'Data2']; // Won't work.
end;

然而,在Delphi的新版本,它几乎是可能的:

type
  TStringDynArray = array of string;

function MyFunc: TStringDynArray;
begin
  result := TStringDynArray.Create('A', 'B');
end;

最后,

function MyFunc: TStringDynArray;
const
  CDynData: array[0..1] of string = ('Data1', 'Data2');
begin
  result := CDynData;
end;

不会起作用,因为TStringDynArray是一个动态数组 ,而CDynData是一个静态数组 ,这是两个不同的基本类型。



Answer 2:

这种结构

['string1', 'string2'] 

已知为开放数组构造 。 从文档 :

开放数组构造函数允许你直接在功能和过程调用构建阵列。

它们可以通过只开放数组参数或变体开放数组参数。

所以,你不能用一个开放数组构造函数来创建一个函数的返回值。


如果您有需要返回数组中元素的固定数量,你可以使用一个动态数组构造函数:

Result := TStringDynArray.Create('string1', 'string2');

然而,这不会对元素的可变数量的工作。 现在,我知道你的问题的例子只有数组中元素的常数。 但我敢肯定,你会遇到需要更多的灵活性比动态数组构造能够提供的情况。

如果你想创建一个现有的动态数组的副本,并返回,使用Copy

Result := Copy(SomeOtherDynamicArray);

这打破了,当你手头有一个开放的阵列。 你可以不通过开放数组Copy 。 我个人认为这是相当可惜,因为开放数组参数是如此的格外灵活和有用,我希望看到他们尽可能多的RTL的支持成为可能。

所以,你最终不得不编写辅助功能对于这种情况。 你可以写每个数组类型专用的帮手,但是变得有些烦人。 这就是仿制药派上用场。 我有目的的辅助类。 下面是相关的摘录:

type
  TArray = class(Generics.Collections.TArray)
    ....
    class function Copy<T>(const Source: array of T): TArray<T>; overload; static;
    ....
  end;

class function TArray.Copy<T>(const Source: array of T): TArray<T>;
var
  i: Integer;
begin
  SetLength(Result, Length(Source));
  for i := 0 to high(Result) do begin
    Result[i] := Source[i];
  end;
end;

现在,这与您的字符串数组,也可与任何其他类型。 这样称呼它:

Result := TArray.Copy<string>(SomeStringOpenArray);

甲临界点要提出的是,我们使用的是通用版本的动态数组, TArray<string>而非TStringDynArray 。 这是至关重要的,你这样做,如果你要认真使用泛型。 这是因为TStringDynArray不分配兼容TArray<string>或的确声明为任何其他类型的array of string 。 它支付股息改变你的代码库使用TArray<T>贯穿始终。

万一有人有兴趣在这个助手类的其余部分,在这里它是:

type
  TArray = class(Generics.Collections.TArray)
  private
    class function Comparison<T>(SortType: TSortType): TComparison<T>; static;
    class function Comparer<T>(const Comparison: TComparison<T>): IComparer<T>; static;
  public
    class procedure Swap<T>(var Left, Right: T); static;
    class procedure Reverse<T>(var Values: array of T); static;
    class function Reversed<T>(const Values: array of T): TArray<T>; static;
    class function Contains<T>(const Values: array of T; const Item: T; out ItemIndex: Integer): Boolean; overload; static;
    class function Contains<T>(const Values: array of T; const Item: T): Boolean; overload; static;
    class function IndexOf<T>(const Values: array of T; const Item: T): Integer; static;
    class function Sorted<T>(var Values: array of T; SortType: TSortType; Index, Count: Integer): Boolean; overload; static;
    class function Sorted<T>(var Values: array of T; SortType: TSortType): Boolean; overload; static;
    class function Sorted<T>(var Values: array of T; const Comparison: TComparison<T>; Index, Count: Integer): Boolean; overload; static;
    class function Sorted<T>(var Values: array of T; const Comparison: TComparison<T>): Boolean; overload; static;
    class function Sorted<T>(GetValue: TFunc<Integer,T>; const Comparison: TComparison<T>; Index, Count: Integer): Boolean; overload; static;
    class procedure Sort<T>(var Values: array of T; SortType: TSortType; Index, Count: Integer); overload; static;
    class procedure Sort<T>(var Values: array of T; SortType: TSortType); overload; static;
    class procedure Sort<T>(var Values: array of T; const Comparison: TComparison<T>; Index, Count: Integer); overload; static;
    class procedure Sort<T>(var Values: array of T; const Comparison: TComparison<T>); overload; static;
    class function Copy<T>(const Source: array of T; Index, Count: Integer): TArray<T>; overload; static;
    class function Copy<T>(const Source: array of T): TArray<T>; overload; static;
    class procedure Move<T>(const Source: array of T; var Dest: array of T; Index, Count: Integer); overload; static;
    class procedure Move<T>(const Source: array of T; var Dest: array of T); overload; static;
    class function Concatenated<T>(const Source1, Source2: array of T): TArray<T>; overload; static;
    class function Concatenated<T>(const Source: array of TArray<T>): TArray<T>; overload; static;
    class procedure Initialise<T>(var Values: array of T; const Value: T); static;
    class procedure Zeroise<T>(var Values: array of T); static;
    class function GetHashCode<T>(const Values: array of T): Integer; overload; static;
    class function GetHashCode<T>(Values: Pointer; Count: Integer): Integer; overload; static;
  end;

class function TArray.Comparison<T>(SortType: TSortType): TComparison<T>;
var
  DefaultComparer: IComparer<T>;
begin
  DefaultComparer := TComparer<T>.Default;
  Result :=
    function(const Left, Right: T): Integer
    begin
      case SortType of
      stIncreasing:
        Result := DefaultComparer.Compare(Left, Right);
      stDecreasing:
        Result := -DefaultComparer.Compare(Left, Right);
      else
        RaiseAssertionFailed(Result);
      end;
    end;
end;

class function TArray.Comparer<T>(const Comparison: TComparison<T>): IComparer<T>;
begin
  Result := TComparer<T>.Construct(Comparison);
end;

class procedure TArray.Swap<T>(var Left, Right: T);
var
  temp: T;
begin
  temp := Left;
  Left := Right;
  Right := temp;
end;

class procedure TArray.Reverse<T>(var Values: array of T);
var
  bottom, top: Integer;
begin
  bottom := 0;
  top := high(Values);
  while top>bottom do begin
    Swap<T>(Values[bottom], Values[top]);
    inc(bottom);
    dec(top);
  end;
end;

class function TArray.Reversed<T>(const Values: array of T): TArray<T>;
var
  i, j, Count: Integer;
begin
  Count := Length(Values);
  SetLength(Result, Count);
  j := Count-1;
  for i := 0 to Count-1 do begin
    Result[i] := Values[j];
    dec(j);
  end;
end;

class function TArray.Contains<T>(const Values: array of T; const Item: T; out ItemIndex: Integer): Boolean;
var
  DefaultComparer: IEqualityComparer<T>;
  Index: Integer;
begin
  DefaultComparer := TEqualityComparer<T>.Default;
  for Index := 0 to high(Values) do begin
    if DefaultComparer.Equals(Values[Index], Item) then begin
      ItemIndex := Index;
      Result := True;
      exit;
    end;
  end;
  ItemIndex := -1;
  Result := False;
end;

class function TArray.Contains<T>(const Values: array of T; const Item: T): Boolean;
var
  ItemIndex: Integer;
begin
  Result := Contains<T>(Values, Item, ItemIndex);
end;

class function TArray.IndexOf<T>(const Values: array of T; const Item: T): Integer;
begin
  Contains<T>(Values, Item, Result);
end;

class function TArray.Sorted<T>(var Values: array of T; SortType: TSortType; Index, Count: Integer): Boolean;
begin
  Result := Sorted<T>(Values, Comparison<T>(SortType), Index, Count);
end;

class function TArray.Sorted<T>(var Values: array of T; SortType: TSortType): Boolean;
begin
  Result := Sorted<T>(Values, Comparison<T>(SortType));
end;

class function TArray.Sorted<T>(var Values: array of T; const Comparison: TComparison<T>; Index, Count: Integer): Boolean;
var
  i: Integer;
begin
  for i := Index+1 to Index+Count-1 do begin
    if Comparison(Values[i-1], Values[i])>0 then begin
      Result := False;
      exit;
    end;
  end;
  Result := True;
end;

class function TArray.Sorted<T>(var Values: array of T; const Comparison: TComparison<T>): Boolean;
begin
  Result := Sorted<T>(Values, Comparison, 0, Length(Values));
end;

class function TArray.Sorted<T>(GetValue: TFunc<Integer, T>; const Comparison: TComparison<T>; Index, Count: Integer): Boolean;
var
  i: Integer;
begin
  for i := Index+1 to Index+Count-1 do begin
    if Comparison(GetValue(i-1), GetValue(i))>0 then begin
      Result := False;
      exit;
    end;
  end;
  Result := True;
end;

class procedure TArray.Sort<T>(var Values: array of T; SortType: TSortType; Index, Count: Integer);
begin
  Sort<T>(Values, Comparison<T>(SortType), Index, Count);
end;

class procedure TArray.Sort<T>(var Values: array of T; SortType: TSortType);
begin
  Sort<T>(Values, SortType, 0, Length(Values));
end;

class procedure TArray.Sort<T>(var Values: array of T; const Comparison: TComparison<T>; Index, Count: Integer);
begin
  if not Sorted<T>(Values, Comparison, Index, Count) then begin
    Sort<T>(Values, Comparer<T>(Comparison), Index, Count);
  end;
end;

class procedure TArray.Sort<T>(var Values: array of T; const Comparison: TComparison<T>);
begin
  Sort<T>(Values, Comparison, 0, Length(Values));
end;

class function TArray.Copy<T>(const Source: array of T; Index, Count: Integer): TArray<T>;
var
  i: Integer;
begin
  SetLength(Result, Count);
  for i := 0 to high(Result) do begin
    Result[i] := Source[i+Index];
  end;
end;

class function TArray.Copy<T>(const Source: array of T): TArray<T>;
var
  i: Integer;
begin
  SetLength(Result, Length(Source));
  for i := 0 to high(Result) do begin
    Result[i] := Source[i];
  end;
end;

class procedure TArray.Move<T>(const Source: array of T; var Dest: array of T; Index, Count: Integer);
var
  i: Integer;
begin
  for i := 0 to Count-1 do begin
    Dest[i] := Source[i+Index];
  end;
end;

class procedure TArray.Move<T>(const Source: array of T; var Dest: array of T);
var
  i: Integer;
begin
  for i := 0 to high(Source) do begin
    Dest[i] := Source[i];
  end;
end;

class function TArray.Concatenated<T>(const Source1, Source2: array of T): TArray<T>;
var
  i, Index: Integer;
begin
  SetLength(Result, Length(Source1)+Length(Source2));
  Index := 0;
  for i := low(Source1) to high(Source1) do begin
    Result[Index] := Source1[i];
    inc(Index);
  end;
  for i := low(Source2) to high(Source2) do begin
    Result[Index] := Source2[i];
    inc(Index);
  end;
end;

class function TArray.Concatenated<T>(const Source: array of TArray<T>): TArray<T>;
var
  i, j, Index, Count: Integer;
begin
  Count := 0;
  for i := 0 to high(Source) do begin
    inc(Count, Length(Source[i]));
  end;
  SetLength(Result, Count);
  Index := 0;
  for i := 0 to high(Source) do begin
    for j := 0 to high(Source[i]) do begin
      Result[Index] := Source[i][j];
      inc(Index);
    end;
  end;
end;

class procedure TArray.Initialise<T>(var Values: array of T; const Value: T);
var
  i: Integer;
begin
  for i := 0 to high(Values) do begin
    Values[i] := Value;
  end;
end;

class procedure TArray.Zeroise<T>(var Values: array of T);
begin
  Initialise<T>(Values, Default(T));
end;

{$IFOPT Q+}
  {$DEFINE OverflowChecksEnabled}
  {$Q-}
{$ENDIF}
class function TArray.GetHashCode<T>(const Values: array of T): Integer;
// see http://stackoverflow.com/questions/1646807 and http://stackoverflow.com/questions/11294686
var
  Value: T;
  EqualityComparer: IEqualityComparer<T>;
begin
  EqualityComparer := TEqualityComparer<T>.Default;
  Result := 17;
  for Value in Values do begin
    Result := Result*37 + EqualityComparer.GetHashCode(Value);
  end;
end;

class function TArray.GetHashCode<T>(Values: Pointer; Count: Integer): Integer;
// see http://stackoverflow.com/questions/1646807 and http://stackoverflow.com/questions/11294686
var
  Value: ^T;
  EqualityComparer: IEqualityComparer<T>;
begin
  EqualityComparer := TEqualityComparer<T>.Default;
  Result := 17;
  Value := Values;
  while Count>0 do begin
    Result := Result*37 + EqualityComparer.GetHashCode(Value^);
    inc(Value);
    dec(Count);
  end;
end;
{$IFDEF OverflowChecksEnabled}
  {$Q+}
{$ENDIF}


Answer 3:

有问题

function MyFunc: TStringDynArray;
begin
  Result := ['Data1', 'Data2'];
end;

['Data1', 'Data2']被解释为一个组 。

我有时会使用以下便利功能(但通常不是在性能关键部分):

function MakeStringArray(const Strings: array of string): TStringDynArray;
var
  i: Integer;
begin
  SetLength(Result, Length(Strings));
  for i := Low(Strings) to High(Strings) do
    Result[i] := Strings[i];
end {MakeStringArray};

利用这一点,你可以重写你的第一个例子如下:

function MyFunc: TStringDynArray;
....
function MyFunc: TStringDynArray;
begin
    Result := MakeStringArray(['Data1', 'Data2']);
end;

但是,由于您使用的XE3,你就要去使用更好的TStringDynArray.Create ,如安德烈亚斯Rejbrand 建议 。



文章来源: Why can't I return arbitrary array of string?