【问题标题】:Why casting an open array parameter to an array type causes E2089 Invalid typecast?为什么将开放数组参数转换为数组类型会导致 E2089 类型转换无效?
【发布时间】:2018-05-18 15:04:47
【问题描述】:

我正在使用 Delphi 2007(Pre generics),并且我定义了许多可用于 TObject 后代的所有数组的函数,例如:

function IndexOf(AArray : array of TObject; AItem : TObject) : integer;
begin
  //...
end;

为了向它们传递TObject 的后代的动态数组,我定义了一个数组类型TObjectArray = array of TObject。通过这种方式,我可以毫无问题地转换动态数组并将它们传递给我的函数:

type
  TChild = class(TObject);

...

procedure Test();
var
  Items : array of TChild;
  Item : TChild;
begin
  //...
  IndexOf(TObjectArray(Items), Item);
end;

当我尝试向它们传递开放数组参数时,问题就来了:

procedure  Test(AItems : array of TChild);
var
  Item : TChild;
begin
  //...
  IndexOf(TObjectArray(AItems), Item);
end;

在这些情况下,编译器会引发以下错误消息:

E2089 类型转换无效

为什么会发生这种情况,我该如何避免?

【问题讨论】:

  • 按值传递会复制整个数组。

标签: arrays delphi casting delphi-2007 open-array-parameters


【解决方案1】:

在将任何类型的数组传递给开放数组参数时,您不需要进行类型转换,只要元素类型相同。您可以按原样传递数组,打开的数组会很好地接受它。这就是开放数组的全部意义所在。

type
  TChild = class(TObject);

...

function IndexOf(AArray : array of TObject; AItem : TObject) : integer;
begin
  //...
end;

procedure Test();
var
  Items : array of TObject;
  Item : TChild;
begin
  //...
  IndexOf(Items, Item);
end;

procedure Test2();
var
  Items : array[0..N] of TObject;
  Item : TChild;
begin
  //...
  IndexOf(Items, Item);
end;

procedure Test3(AItems : array of TObject);
var
  Item : TChild;
begin
  //...
  IndexOf(AItems, Item);
end;

但是,您不能将 TChild 的数组传递给需要 TObject 的数组。编译器会以“不兼容的类型”错误拒绝它。输入数组必须使用与打开数组相同的元素类型。

在传递动态数组或固定数组时,一个简单的类型转换可以解决这个问题:

procedure Test();
type
  TObjectArray = array of TObject;
var
  Items : array of TChild;
  Item : TChild;
begin
  //...
  IndexOf(TObjectArray(Items), Item);
end;

procedure Test2();
type
  TObjectFixedArray = array[0..N] of TObject;
  PObjectFixedArray = ^TObjectFixedArray;
var
  Items : array[0..N] of TChild;
  Item : TChild;
begin
  //...
  IndexOf(PObjectFixedArray(@Items)^, Item);
end;

但是,您根本无法将开放数组类型转换为任何其他数组类型。不同类型的数组有不同的内存布局(将一个动态数组类型转换为另一个动态数组,或者将一个固定数组类型转换为另一个固定数组,不会改变被类型转换数组的内存布局)。

在开放数组的情况下,它其实根本就不是一个数组,它只是一个指向传入数组的第一个元素的指针,还有一个数组长度的第二个隐藏参数。换句话说,这种声明:

procedure Test3(AItems : array of TChild);

其实是编译器在后台实现的这样:

procedure Test3(AItems : ^TChild; AItems_High: Integer);

因此,您必须将打开的数组元素复制到另一个数组,然后传递该数组:

procedure Test3(AItems : array of TChild);
var
  Items: array of TObject;
  Item : TChild;
  I: Integer;
begin
  //...
  SetLength(Items, Length(AItems));
  For I := Low(AItems) to High(AItems) do
    Items[I] := AItems[I];
  IndexOf(Items, Item);
end;

【讨论】:

  • 我已经更改了IndexOf(AItems, Item); 中的代码,但它给出了E2008 不兼容的类型
猜你喜欢
  • 1970-01-01
  • 2013-05-01
  • 2011-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多