我将维护并移植到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
。从1
到aSV.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等等。但我想你会发现它很有用。它确实展示了如何使一个对象看起来非常像一个下界非零的数组。