动态数组是否支持非零下界(用于VarArrayCreate兼容性)



我将维护并移植到Delphi XE2一堆非常旧的Delphi代码,其中充满了VarArrayCreate构造,以伪造具有下界不为零的动态数组。

使用变体类型的缺点是:

  • 比本地数组慢一些(代码做了很多复杂的财务计算,所以速度很重要)
  • 不是类型安全的(特别是当意外使用了错误的var...常数,并且Variant系统开始进行不必要的转换或舍入时)

如果我可以使用动态数组,这两个都可以变得没有意义。

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

我记得的是,动态数组过去总是从0的下界开始。

这仍然是真的吗?换句话说:是否有可能让动态数组从不同于零的边界开始?

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

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的空格零元素交换为改进的编译时检查:

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;

动态数组总是有0的下界。所以,对于所有的动态数组,low(A)等于0。对于空的动态数组也是如此,例如nil

来自文档:

动态数组总是以整数为索引,总是从0开始。

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

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,一个copy to,一个copy from等等。但我想你会发现它很有用。它确实展示了如何使一个对象看起来非常像一个下界非零的数组。

相关内容

  • 没有找到相关文章

最新更新