如何使局部数组变量有条件地指向开放数组参数?

How to make a local array variable conditionally points to an open array parameter?

我正在尝试有条件地分配一个本地开放数组变量,以便它指向一个常量开放数组参数:

uses
  System.Generics.Collections,
  System.Generics.Defaults;
    
type
  TArray = class(System.Generics.Collections.TArray)
  public
    class function  SameValues<T>(const AValuesA: array of T; AValuesB: array of T; const AComparer: IEqualityComparer<T>; AOrderMatters : Boolean = True) : Boolean; overload; static;
  end;
    
...
    
class function TArray.SameValues<T>(const AValuesA: array of T; AValuesB: array of T; const AComparer: IEqualityComparer<T>; AOrderMatters : Boolean = True) : Boolean;
var
  ArrA : TArray<T>;
  ArrB : TArray<T>;
  i : integer;
begin
  //checking sizes
  if(Length(AValuesA) <> Length(AValuesB)) then
  begin
    Result := False;
    Exit;
  end;

  if(AOrderMatters) then
  begin
    //I don't need to change the arrays, so I could directly point to the open array parameters
    ArrA := AValuesA;
    ArrB := AValuesB;
  end else
  begin
    //copying to local arrays
    SetLength(ArrA, Length(AValuesA));
    TArray.Copy<T>(AValuesA, ArrA, Length(AValuesA));
    SetLength(ArrB, Length(AValuesB));
    TArray.Copy<T>(AValuesB, ArrB, Length(AValuesB));

    //sorting local arrays
    TArray.Sort<T>(ArrA);
    TArray.Sort<T>(ArrB);
  end;

  //comparing elements
  i := 0;
  while(i < Length(ArrA)) do
  begin
    if(not AComparer.Equals(ArrA[i], ArrB[i])) then
    begin
      Result := False;
      Exit;
    end;
    Inc(i);
  end;
  Result := True;
end;

编译时,在以下位置引发 E2010 错误:

//I don't need to change the arrays, so I could directly point to the open array parameters
ArrA := AValuesA;
ArrB := AValuesB;

E2010 Incompatible types: 'Dynamic array' and 'array of T'

我试过以下方法:

ArrA := @AValuesA;
ArrB := @AValuesB;

它编译但随后在运行时引发 AV 异常(最重要的是,我不知道这是否是一种安全的方法)。

这是我的测试应用程序代码:

uses
  System.Generics.Defaults;

procedure TForm1.FormCreate(Sender: TObject);
var
  ArrA : array of string;
  ArrB : array of string;
begin
  SetLength(ArrA, 2);
  ArrA[0] := 'hello';
  ArrA[1] := 'world';

  SetLength(ArrB, 2);
  ArrB[0] := 'world';
  ArrB[1] := 'hello';

  if(TArray.SameValues<string>(ArrA, ArrB, TEqualityComparer<string>.Default, True)) then
    ShowMessage('Same values and same order')
  else if(TArray.SameValues<string>(ArrA, ArrB, TEqualityComparer<string>.Default, False)) then
    ShowMessage('Same values but different order')
  else
    ShowMessage('Different values');
end;

您尝试执行的操作是不可能的,因为开放数组和动态数组与您的代码尝试的方式不兼容。

不过,有一种简单的方法可以实现您想要的效果,即使用简单的递归。像这样:

class function TArray.SameValues<T>(const AValuesA: array of T; AValuesB: array of T; const AComparer: IEqualityComparer<T>; AOrderMatters : Boolean = True) : Boolean;
var
  ArrA : TArray<T>;
  ArrB : TArray<T>;
  i : integer;
begin
  //checking sizes
  if Length(AValuesA) <> Length(AValuesB) then
  begin
    Result := False;
    Exit;
  end;

  if not AOrderMatters then
  begin
    //copying to local arrays
    SetLength(ArrA, Length(AValuesA));
    TArray.Copy<T>(AValuesA, ArrA, Length(AValuesA));
    SetLength(ArrB, Length(AValuesB));
    TArray.Copy<T>(AValuesB, ArrB, Length(AValuesB));

    //sorting local arrays
    TArray.Sort<T>(ArrA);
    TArray.Sort<T>(ArrB);

    Result := SameValues<T>(ArrA, ArrB, AComparer, True);
    Exit;
  end;

  //comparing elements
  for i := 0 to High(AValuesA) do
  begin
    if not AComparer.Equals(AValuesA[i], AValuesB[i]) then
    begin
      Result := False;
      Exit;
    end;
  end;

  Result := True;
end;