你可以在通用类型中封装你想要的东西。就像这样:
type
TFixedLengthArray<T> = record
strict private
FItems: TArray<T>;
FLength: Integer;
function GetItem(Index: Integer): T; inline;
procedure SetItem(Index: Integer; const Value: T); inline;
public
property Length: Integer read FLength;
property Items[Index: Integer]: T read GetItem write SetItem; default;
class function New(const Values: array of T): TFixedLengthArray<T>; static;
end;
{ TFixedLengthArray<T> }
class function TFixedLengthArray<T>.New(const Values: array of T): TFixedLengthArray<T>;
var
i: Integer;
begin
Result.FLength := System.Length(Values);
SetLength(Result.FItems, Result.FLength);
for i := 0 to Result.FLength-1 do begin
Result.FItems[i] := Values[i];
end;
end;
function TFixedLengthArray<T>.GetItem(Index: Integer): T;
begin
Result := FItems[Index];
end;
procedure TFixedLengthArray<T>.SetItem(Index: Integer; const Value: T);
begin
FItems[Index] := Value;
end;
创建一个新的一个是这样的:
var
MyArray: TFixedLengthArray<Integer>;
....
MyArray: TFixedLengthArray<Integer>.New([1, 42, 666]);
Access项目中是这样的:
for i := 0 to MyArray.Length-1 do
Writeln(MyArray[i]);
这只是包装一个动态数组。元素是连续的。数组的长度决定一劳永逸,然后创建一个新的实例。
有一点要注意这里是该类型将像一个引用类型,因为它的数据存储在引用类型。也就是说,这种类型的赋值运算符的行为方式与动态数组赋值相同。
因此,如果我们有这种类型的两个变量,arr1
和arr2
然后会发生以下情况:
arr1 := arr2;
arr1[0] := 42;
Assert(arr2[0] = 42);
如果你想使型表现得像一个真正的价值,那么你会实现写入时复制里面有SetItem
。
更新
你编辑的问题变化显著的。事实上,你似乎更关心性能而不是封装。
在上述类型的项的存取方法的内联意味着性能特性应接近阵列。访问仍然是O(1)
,但内联/优化器很弱并且无法发出最优代码是非常合理的。
在你决定,你必须使用数组,以获得绝对极致的表现,做一些真实世界的标杆。在我看来,从阵列读取/写入的代码确实是一个瓶颈的可能性非常小。最有可能的瓶颈将是你对数组中的值做什么。
不,没有。它不再是静态的。 – TLama
那么究竟是什么阻止我在运行时初始化静态数组?在编译的硬编码内存块和运行时生成的“保证内存块”数组之间有什么不同? – Art1st
@ Art1st如果初始化(包括分配)发生在您自己的代码中,那必然意味着在可以运行的初始化之前可能存在代码*。 (例如,如果您有两个这样的数组,则一个初始化必须在另一个之前执行。)如果其他代码访问尚未初始化的数组,则会发生什么情况?无论你的答案是什么,这是否意味着数组更像静态数组,或更像是一个动态数组? (我强烈怀疑后者。) – hvd