代码之家  ›  专栏  ›  技术社区  ›  Fabrizio RAHUL S R

为什么将打开的数组参数强制转换为数组类型会导致e2089无效的类型转换?

  •  5
  • Fabrizio RAHUL S R  · 技术社区  · 7 年前

    我在用 德尔菲2007 (前泛型)并且我定义了许多函数,这些函数可以用于 TObject 的后代,例如:

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

    传递它们的动态数组 目标 的后代,我定义了一个数组类型 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无效的类型转换

    为什么会发生这种情况,我怎么能避免呢?

    1 回复  |  直到 6 年前
        1
  •  6
  •   Remy Lebeau    7 年前

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

    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;