问题描述
如何让变量指向记录数组?
注意:我想将 TRecord 的预定义数组作为常量... 但是在代码中,我需要变量 'W' 来记录要使用的记录数组。
请注意,我不希望使用 TRecord 的构造函数在代码中(动态地)创建 TRecord 数组,而是希望拥有静态数组(因为数据不会改变)。
如何让变量“W”“记录”TRecord 的哪个数组?
请看下面的代码 - 更容易理解我的意思。
procedure TForm1.Button1Click(Sender: TObject);
type
TTestRec = record
X: string;
Y: Integer;
end;
TMyArr = TArray<TTestRec>;
const
ARRAY_A : TArray<string> = ['A1','A2','A3','A4'];
ARRAY_B : TArray<string> = ['B1','B2','B3'];
ARRAY_C : array[1..2] of TTestRec = (
(X: 'testC1'; Y:1),(X: 'testC2'; Y:2)
);
ARRAY_D : array[1..3] of TTestRec = (
(X: 'testD1'; Y:3),(X: 'testD2'; Y:4)
(X: 'testD3'; Y:9)
);
var
Z : TArray<string>;
W : array of TTestRec;
begin
Z := ARRAY_A; // this works
Z := ARRAY_B; // this works
W := ARRAY_C; // this does not work
W := ARRAY_D; // this does not work
end;
解决方法
将 ARRAY_A
或 ARRAY_B
分配给 Z
有效,因为您将 TArray<string>
常量分配给 TArray<string>
变量。它们都是相同的类型,因此可以相互兼容。
将 ARRAY_C
或 ARRAY_D
分配给 W
不起作用,因为您将静态数组常量分配给动态数组变量。它们是彼此不兼容的不同类型,如 Delphi 的文档中所述:
Type Compatibility and Identity (Delphi)
类型兼容性
每种类型都与自身兼容。如果两个不同的类型至少满足以下条件之一,则它们兼容。
- 它们都是真实的类型。
- 它们都是整数类型。
- 一种类型是另一种类型的子范围。
- 两种类型都是同一类型的子范围。
- 两者都是具有兼容基类型的集合类型。
- 两者都是具有相同字符数的压缩字符串类型。
- 一种是字符串类型,另一种是字符串、压缩字符串或
Char
类型。 - 一种类型是
Variant
,另一种是整数、实数、字符串、字符或布尔类型。 - 两者都是类、类引用或接口类型,一种类型派生自另一种类型。
- 一种类型是
PAnsiChar
或PWideChar
,另一种是array[0..n] of PAnsiChar
或PWideChar
形式的从零开始的字符数组。 - 一种类型是
Pointer
(无类型指针),另一种是任何指针类型。 - 两种类型都是指向同一类型的(类型化)指针,并且
{$T+}
编译器指令有效。 - 两者都是过程类型,具有相同的结果类型、相同的参数数量以及相应位置的参数之间的类型标识。
作业兼容性
分配兼容性不是对称关系。如果表达式的值落在 T1 的范围内并且至少满足以下条件之一,则可以将类型为 T2 的表达式分配给类型为 T1 的变量:
- T1 和 T2 属于同一类型,并且不是包含任何级别的文件类型的文件类型或结构化类型。
- T1 和 T2 是兼容的序数类型。
- T1 和 T2 都是实数类型。
- T1 是实数类型,T2 是整数类型。
- T1 为
PAnsiChar
、PWideChar
、PChar
或任何字符串类型,且表达式为字符串常量。 - T1 和 T2 都是字符串类型。
- T1 是字符串类型,T2 是
Char
或压缩字符串类型。 - T1 是一个长字符串,T2 是
PAnsiChar
、PWideChar
或PChar
。 - T1 和 T2 是兼容的压缩字符串类型。
- T1 和 T2 是兼容的集合类型。
- T1 和 T2 是兼容的指针类型。
- T1 和 T2 都是类、类引用或接口类型,T2 是从 T1 派生的。
- T1 是接口类型,T2 是实现 T1 的类类型。
- T1 是
PAnsiChar
或PWideChar
,T2 是array[0..n] of Char
形式(当 T1 为PAnsiChar
)或WideChar
形式的从零开始的字符数组(当 T1 为PWideChar
时)。 - T1 和 T2 是兼容的程序类型。 (在某些赋值语句中,函数或过程标识符被视为过程类型的表达式。请参阅本章前面的“语句和表达式中的过程类型”。)
- T1 是
Variant
,T2 是整数、实数、字符串、字符、布尔值、接口类型或OleVariant
类型。 - T1 是
OleVariant
,T2 是整数、实数、字符串、字符、布尔值、接口或Variant
类型。 - T1 是整数、实数、字符串、字符或布尔类型,T2 是
Variant
或OleVariant
。 - T1 是
IUnknown
或IDispatch
接口类型,T2 是Variant
或OleVariant
。 (如果 T1 是varEmpty
,则变体的类型代码必须是varUnknown
、varDispatch
或IUnknown
,如果 T1 是varEmpty
,则必须是varDispatch
或IDispatch
{1}}。)
将 ARRAY_A
或 ARRAY_B
分配给 Z
满足“分配兼容性”要求。将 ARRAY_C
或 ARRAY_D
分配给 W
不会。
要解决静态数组的问题,您必须改用指针(动态数组已经是指针),例如:
procedure TForm1.Button1Click(Sender: TObject);
type
TTestRec = record
X: string;
Y: Integer;
end;
PTestRec = ^TTestRec;
const
ARRAY_A : TArray<string> = ['A1','A2','A3','A4'];
ARRAY_B : TArray<string> = ['B1','B2','B3'];
ARRAY_C : array[1..2] of TTestRec = (
(X: 'testC1'; Y:1),(X: 'testC2'; Y:2)
);
ARRAY_D : array[1..3] of TTestRec = (
(X: 'testD1'; Y:3),(X: 'testD2'; Y:4)
(X: 'testD3'; Y:9)
);
var
Z : TArray<string>;
W : PTestRec;
begin
Z := ARRAY_A;
Z := ARRAY_B;
W := @ARRAY_C[1];
W := @ARRAY_D[1];
end;
但是请注意,无法从 W
本身确定它是指向 array[1..2] of TTestRec
还是 array[1..3] of TTestRec
,这是两种完全不同的类型。因此,如果您需要使用 W
来迭代数组,则必须单独跟踪可接受的边界,例如:
{$POINTERMATH ON}
procedure TForm1.Button1Click(Sender: TObject);
type
TTestRec = record
X: string;
Y: Integer;
end;
PTestRec = ^TTestRec;
const
ARRAY_A : TArray<string> = ['A1',(X: 'testD2'; Y:4)
(X: 'testD3'; Y:9)
);
var
Z : TArray<string>;
W : PTestRec;
W_Len,I: Integer;
begin
Z := ARRAY_A;
for I := 0 to High(Z) do begin
// use Z[I] as needed ...
end;
Z := ARRAY_B;
for I := 0 to High(Z) do begin
// use Z[I] as needed ...
end;
W := @ARRAY_C[1];
W_Len := Length(ARRAY_C);
for I := 0 to Pred(W_Len) do begin
// use W[I] as needed ...
end;
W := @ARRAY_D[1];
W_Len := Length(ARRAY_D);
for I := 0 to Pred(W_Len) do begin
// use W[I] as needed ...
end;
end;
,
Remy 已经解释了您的问题,但我想提出一个替代答案。
您不能将静态数组分配给动态数组,但您可以将动态数组隐藏在记录中并使该记录执行您要执行的操作。
每个静态数组都不同,因此您需要单独处理每个数组,但由于您想使用常量数组,我猜不同长度的数量是有限的。
这是对处理我所谈论内容的代码的重写。例行测试在外观上与您的示例非常相似。
unit UnitTest;
interface
implementation
type
TTestRec = record
X: string;
Y: Integer;
end;
TDArray = array of TTestRec;
TSArray2 = array[1..2] of TTestRec;
TSArray3 = array[1..3] of TTestRec;
TMyArr = record
Items : array of TTestRec;
private
function GetItem(const i: integer): TTestRec;
procedure SetItem(const i: integer; const Value: TTestRec);
function GetCount: integer;
public
class operator implicit (a : TDArray ) : TMyArr;
class operator implicit (a : TMyArr ) : TDArray;
class operator implicit (a : TSArray2 ) : TMyArr;
class operator implicit (a : TSArray3 ) : TMyArr;
property Item[ const i : integer ] : TTestRec
read GetItem
write SetItem; default;
property Count : integer
read GetCount;
end;
const
ARRAY_A : TArray<string> = ['A1','B3'];
ARRAY_C : TSArray2 = (
(X: 'testC1'; Y:1),(X: 'testC2'; Y:2)
);
ARRAY_D : TSArray3 = (
(X: 'testD1'; Y:3),(X: 'testD2'; Y:4),(X: 'testD3'; Y:9)
);
var
Z : TArray<string>;
W : TMyArr;
procedure Test;
var
iTest : TTestRec;
i : integer;
begin
Z := ARRAY_A; // this works
Z := ARRAY_B; // this works
ARRAY_A[ 1 ] := 'Fred';
W := ARRAY_C; // this does not work
iTest := W[1];
W := ARRAY_D; // this does not work
iTest := W[ 1 ];
// iteration
for i := 0 to W.Count - 1 do
begin
iTest := W[i]; //etc
end;
end;
{ TMyArr }
class operator TMyArr.implicit(a: TMyArr): TDArray;
var
i: Integer;
begin
SetLength( Result,Length( a.Items ) );
for i := 0 to Length( a.Items ) - 1 do
begin
Result[ i ] := a.Items[ i ];
end;
end;
class operator TMyArr.implicit(a: TDArray): TMyArr;
var
i: Integer;
begin
SetLength( Result.Items,Length( a ));
for i := 0 to Length( a ) - 1 do
begin
Result.Items[ i ] := a[i + 1]; //not zero based!
end;
end;
class operator TMyArr.implicit(a: TSArray2): TMyArr;
var
i: Integer;
begin
SetLength( Result.Items,Length(a));
for i := 0 to Length(a) - 1 do
begin
Result.Items[i] := a[i + 1];
end;
end;
function TMyArr.GetCount: integer;
begin
Result := Length( Items );
end;
function TMyArr.GetItem(const i: integer): TTestRec;
begin
Result := Items[ i ];
end;
class operator TMyArr.implicit(a: TSArray3): TMyArr;
var
i: Integer;
begin
SetLength( Result.Items,Length(a));
for i := 0 to Length(a) - 1 do
begin
Result.Items[i] := a[i];
end;
end;
procedure TMyArr.SetItem(const i: integer; const Value: TTestRec);
begin
Items[ i ] := Value;
end;
end.
编辑
已经展示了如何迭代。我已经添加了一个属性调用计数和显示在测试例程如何遍历