动态数组是否支持非零下限(对于VarArrayCreate兼容性)?

时间:2012-12-20 14:12:39

标签: delphi delphi-xe2 dynamic-arrays variant

我正在维护并向Delphi XE2移植一堆非常旧的Delphi代码,这些代码充满了VarArrayCreate构造,伪造的动态数组的下限不为零。

使用Variant类型的缺点是:

  • 比原生数组慢得多(代码执行了大量复杂的财务计算,因此速度很重要)
  • 不是类型安全的(特别是在使用错误的var...常量时,Variant系统开始进行不需要的转换或舍入)

如果可以使用dynamic arrays,两者都可能没有实际意义。

变量数组的好处是它们可以具有非零下限。

我记得的是,动态数组总是以零的下限开始。

这仍然是真的吗?换句话说:是否可以让动态数组以不同于0的界限开始

作为一个示例,针对特定情况的前/后示例(单维,但代码中包含多维数组,除了varDouble之外,代码还使用了各种其他varXXX数据TVarData允许使用的类型:

function CalculateVector(aSV: TStrings): Variant;
var
  I: Integer;
begin
  Result := VarArrayCreate([1,aSV.Count-1],varDouble);
  for I := 1 to aSV.Count-1 do
    Result[I] := CalculateItem(aSV, I);
end;

CalculateItem函数返回Double。界限从1aSV.Count-1

当前替换是这样的,交换Result的空间第0个元素以改进编译时间检查:

type
  TVector = array of Double;
function CalculateVector(aSV: TStrings): TVector;
var
  I: Integer;
begin
  SetLength(Result, aSV.Count); // lower bound is zero, we start at 1 so we ignore the zeroth element
  for I := 1 to aSV.Count-1 do
    Result[I] := CalculateItem(aSV, I);
end;

2 个答案:

答案 0 :(得分:5)

动态数组的下限始终为0。因此,对于所有动态数组,low(A)等于0。对于空动态数组,即nil

,情况甚至如此

来自documentation

  

动态数组始终是整数索引,始终从0开始。

答案 1 :(得分:2)

已经回答了您的直接问题,我还为您提供了可以在移植中使用的通用类的开头。

type
  TSpecifiedBoundsArray<T> = class
  private
    FValues: TArray<T>;
    FLow: Integer;
    function GetHigh: Integer;
    procedure SetHigh(Value: Integer);
    function GetLength: Integer;
    procedure SetLength(Value: Integer);
    function GetItem(Index: Integer): T;
    procedure SetItem(Index: Integer; const Value: T);
  public
    property Low: Integer read FLow write FLow;
    property High: Integer read GetHigh write SetHigh;
    property Length: Integer read GetLength write SetLength;
    property Items[Index: Integer]: T read GetItem write SetItem; default;
  end;

{ TSpecifiedBoundsArray<T> }

function TSpecifiedBoundsArray<T>.GetHigh: Integer;
begin
  Result := FLow+System.High(FValues);
end;

procedure TSpecifiedBoundsArray<T>.SetHigh(Value: Integer);
begin
  SetLength(FValues, 1+Value-FLow);
end;

function TSpecifiedBoundsArray<T>.GetLength: Integer;
begin
  Result := System.Length(FValues);
end;

procedure TSpecifiedBoundsArray<T>.SetLength(Value: Integer);
begin
  System.SetLength(FValues, Value);
end;

function TSpecifiedBoundsArray<T>.GetItem(Index: Integer): T;
begin
  Result := FValues[Index-FLow];
end;

function TSpecifiedBoundsArray<T>.SetItem(Index: Integer; const Value: T);
begin
  FValues[Index-FLow] := Value;
end;

我认为这很有效。我打算使用record,但我认为这是不可行的。这归结为FLow的值类型语义和FValues的引用类型语义之间的混合。所以,我觉得这里的课程最好。

修改Low时,它的行为也相当奇怪。

毫无疑问,你想延长这一点。您需要添加SetBounds,副本,副本等等。但我认为你可能会发现它很有用。它肯定会显示如何使对象看起来非常像具有非零下限的数组。