你可以在通用類型中封裝你想要的東西。就像這樣:
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