问题
I'm using Delphi 2007 (Pre generics) and I've defined many functions who can be used for all arrays of TObject
's descendants, example:
function IndexOf(AArray : array of TObject; AItem : TObject) : integer;
begin
//...
end;
For passing them dynamic arrays of TObject
's descendants, I've defined an array type TObjectArray = array of TObject
. In this way I can cast dynamic arrays and pass them to my functions without any problems:
type
TChild = class(TObject);
...
procedure Test();
var
Items : array of TChild;
Item : TChild;
begin
//...
IndexOf(TObjectArray(Items), Item);
end;
The problem comes when I try to pass them open array parameters:
procedure Test(AItems : array of TChild);
var
Item : TChild;
begin
//...
IndexOf(TObjectArray(AItems), Item);
end;
In these cases, the compiler raises the following error message:
E2089 Invalid typecast
Why does this happen and how can I avoid that?
回答1:
You don't need to typecast when passing ANY type of array to an open array parameter, provided the elements are the same type. You can pass the array as-is, the open array will accept it just fine. That is the whole point of open arrays.
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;
However, you cannot pass an array of TChild
where an array of TObject
is expected. The compiler will reject it with an "incompatible types" error. The input array must use the same element type as the open array.
A simple typecast can fix that when passing a dynamic array or a fixed array:
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;
But, you simply cannot typecast an open array to any other array type. Different types of arrays have different memory layouts (typecasting a dynamic array to another dynamic array, or a fixed array to another fixed array, does not change the memory layout of the array being typecasted).
In the case of an open array, it is actually not even an array at all, it is just a pointer to the first element of the passed array, and there is a second hidden parameter for the array length. In other words, this kind of declaration:
procedure Test3(AItems : array of TChild);
Is actually implemented by the compiler behind the scenes like this:
procedure Test3(AItems : ^TChild; AItems_High: Integer);
So, you will have to make a copy of the open array elements to another array, and then pass that array along instead:
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;
来源:https://stackoverflow.com/questions/50414260/why-casting-an-open-array-parameter-to-an-array-type-causes-e2089-invalid-typeca