(我已经在CodeReview询问了这个问题,并将其作为主题关闭。希望它在这里的主题。)
我有一个派生类型的静态数组(如下面的示例代码中的LabelsA: array[0..3] of TLabel;
)和一个接受基类型的开放数组的例程(如procedure DoSomethingWithControls(const AControls: array of TControl);
),我想调用DoSomethingWithControls
与那些静态数组。请看我的样本:
procedure DoSomethingWithControls(const AControls: array of TControl);
var
i: Integer;
begin
for i := Low(AControls) to High(AControls) do
Writeln(AControls[i].Name);
end;
procedure Test;
var
LabelsA: array[0..3] of TLabel;
LabelsB: array[0..1] of TLabel;
procedure Variant1;
type
TArray1 = array[Low(LabelsA)..High(LabelsA)] of TControl;
TArray2 = array[Low(LabelsB)..High(LabelsB)] of TControl;
begin
DoSomethingWithControls(TArray1(LabelsA));
DoSomethingWithControls(TArray2(LabelsB));
end;
procedure Variant2;
type
TControlArray = array[0..Pred(MaxInt div SizeOf(TControl))] of TControl;
PControlArray = ^TControlArray;
begin
DoSomethingWithControls(Slice(PControlArray(@LabelsA)^, Length(LabelsA)));
DoSomethingWithControls(Slice(PControlArray(@LabelsB)^, Length(LabelsB)));
end;
procedure Variant3;
var
ControlsA: array[Low(LabelsA)..High(LabelsA)] of TControl absolute LabelsA;
ControlsB: array[Low(LabelsB)..High(LabelsB)] of TControl absolute LabelsB;
begin
DoSomethingWithControls(ControlsA);
DoSomethingWithControls(ControlsB);
end;
begin
Variant1;
Variant2;
Variant3;
end;
调用DoSomethingWithControls
有一些可能的变体:
变体1非常简单,但需要一个"适配器"像TArray1
这样的类型
对于TLabel阵列的每个大小。我希望它更灵活。
变体2更灵活,更均匀,但又丑陋且容易出错。
变式3(由...提供) TOndrej)类似于 变体1 - 它不需要显式转换,但变体1提供了一个 如果你搞砸了一些东西(例如, 复制粘贴时数组边界错误。
我有没有想法如何在没有这些缺点的情况下制定这些调用(不改变数组的元素类型)?它应该适用于D2007和XE6。
答案 0 :(得分:3)
这些演员阵容都很难看。他们都会工作,但使用它们会让你感觉很脏。使用辅助函数是完全合理的:
type
TControlArray = array of TControl;
function ControlArrayFromLabelArray(const Items: array of TLabel): TControlArray;
var
i: Integer;
begin
SetLength(Result, Length(Items));
for i := 0 to high(Items) do
Result[i] := Items[i];
end;
然后你就像这样调用你的函数:
DoSomethingWithControls(ControlArrayFromLabelArray(...));
当然,如果您可以使用泛型,这将更加清洁。
答案 1 :(得分:1)
也不是很漂亮,但你可以像这样欺骗编译器:
<c:when test="${pageCount < maxSuggestPage}">
答案 2 :(得分:1)
声明一个重载过程:
procedure DoSomethingWithControls(const AControls: array of TControl); overload;
var
i: Integer;
begin
for i := 0 to High(AControls) do
if Assigned(AControls[i]) then
Writeln(AControls[i].Name)
else
WriteLn('Control item: ',i);
end;
procedure DoSomethingWithControls(const ALabels: array of TLabel); overload;
type
TControlArray = array[0..Pred(MaxInt div SizeOf(TControl))] of TControl;
PControlArray = ^TControlArray;
begin
DoSomethingWithControls(Slice(PControlArray(@ALabels)^, Length(ALabels)));
end;
这是您的variant2的一般解决方案。所有案例都有一个声明,因此不容易出错。
答案 3 :(得分:1)
下面的示例基于如何在内部实现开放数组参数。它不会使用&#34;键入@运算符&#34;但是。
procedure Variant4;
type
TCallProc = procedure (AControls: Pointer; HighBound: Integer);
var
CallProc: TCallProc;
begin
CallProc := @DoSomethingWithControls;
CallProc(@LabelsA, Length(LabelsA) - 1);
CallProc(@LabelsB, Length(LabelsB) - 1);
end;
只要所有静态数组都基于0,为HighBound传递High(Labels)
可能会更好。
答案 4 :(得分:0)
由于动态数组可以作为开放数组传递给方法,因此选项是将静态数组转换为动态数组。
如果您不介意复制阵列的开销,请考虑以下事项:
编写一个函数将打开的标签数组转换为动态TControlArray数组。
type
TControlArray = array of TControl;
{$IFOPT R+} {$DEFINE R_ON} {$R-} {$ENDIF}
function MakeControlArray(const ALabels: array of TLabel): TControlArray;
begin
SetLength(Result, Length(ALabels));
Move(ALabels[0], Result[0], Length(ALabels) * SizeOf(TObject));
end;
{$IFDEF R_ON} {$R+} {$UNDEF R_ON} {$ENDIF}
现在Variant4可以写成:
procedure Variant4;
begin
DoSomethingWithControls(MakeControlArray(LabelsA));
DoSomethingWithControls(MakeControlArray(LabelsB));
end;
测试用例:
procedure TAdHocTests.TestLabelsToControls;
const
LLabelsA: array[0..3] of TLabel = (Pointer(0),Pointer(1),Pointer(2),Pointer(3));
var
LLoopI: Integer;
LLabelsB: array[0..9] of TLabel;
LEmptyArray: TLabelArray;
begin
for LLoopI := Low(LLabelsB) to High(LLabelsB) do
begin
LLabelsB[LLoopI] := Pointer(LLoopI);
end;
DoSomethingWithControls(MakeControlArray(LLabelsA), Length(LLabelsA));
DoSomethingWithControls(MakeControlArray(LLabelsB), Length(LLabelsB));
DoSomethingWithControls(MakeControlArray([]), 0);
DoSomethingWithControls(MakeControlArray(LEmptyArray), 0);
end;
procedure TAdHocTests.DoSomethingWithControls(
const AControls: array of TControl;
AExpectedLength: Integer);
var
LLoopI: Integer;
begin
CheckEquals(AExpectedLength, Length(AControls), 'Length incorrect');
for LLoopI := Low(AControls) to High(AControls) do
begin
CheckEquals(LLoopI, Integer(AControls[LLoopI]));
end;
end;