Delphi-指向TRecord的常量数组?
如何让变量指向记录数组?
注意:我想将预定义的 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 的文档中所述:
类型兼容性和标识 (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 是
PAnsiCharorPWideChar并且 T2 是形式array[0..n] of Char(当 T1 是PAnsiChar)或 ofWideChar(当 T1 是PWideChar)的从零开始的字符数组。- T1 和 T2 是兼容的程序类型。(在某些赋值语句中,函数或过程标识符被视为过程类型的表达式。请参阅本章前面的“语句和表达式中的过程类型”。)
- T1
Variant和 T2 是整数、实数、字符串、字符、布尔值、接口类型或OleVariant类型。- T1 是一个
OleVariant,T2 是一个整数、实数、字符串、字符、布尔值、接口或Variant类型。- T1 是整数、实数、字符串、字符或布尔类型,T2 是
Variant或OleVariant。- T1 是
IUnknownorIDispatch接口类型,T2 是VariantorOleVariant。(变体的类型代码必须是varEmpty,varUnknown,或者varDispatch如果 T1 是IUnknown,并且varEmpty或者varDispatch如果 T1 是IDispatch。)
分配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自身确定它是指向 anarray[1..2] of TTestRec还是 an 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', '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;
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;