编译器允许我执行以下操作:
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;
这是为什么?这技术上不是一回事吗?
对于这些特定场景,返回任意数组字符串的推荐(和最有效)方法是什么?
答案 0 :(得分:10)
不,这不是一回事。在
procedure MyProc(const ADynData: array of string);
参数是open array parameter,它与'普通'动态数组不同。 [..]
语法只能用于在函数的开放数组参数中创建开放数组。 (否则,[..]
用于在代码中指定集,例如Font.Style := [fsBold, fsItalic]
。但sets只能将序数类型作为其“基本类型”,因此仍然没有'字符串集'这样的东西。)
换句话说,无法在代码中编写动态数组,就像在第二个代码段中尝试一样,
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
是dynamic array,而CDynData
是static array,这是两种不同的基本类型。
答案 1 :(得分:6)
此构造
['string1', 'string2']
被称为open array constructor。来自documentation:
Open数组构造函数允许您直接在函数和过程调用中构造数组。
它们只能作为开放数组参数或变量打开数组参数传递。
因此,您不能使用开放数组构造函数来创建函数返回值。
如果数组中有固定数量的元素需要返回,则可以使用动态数组构造函数:
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}
答案 2 :(得分:2)
的问题
function MyFunc: TStringDynArray;
begin
Result := ['Data1', 'Data2'];
end;
是['Data1', 'Data2']
被解释为set。
我有时使用以下便利功能(但通常不在性能关键部分):
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
,例如Andreas Rejbrand suggests。