Fabrizio
Fabrizio

Reputation: 8043

Is safe to cast a dynamic array before passing it as a variable parameter?

I have some functions which accepts an array of TObject as a var parameter, for example:

type
  TObjectArray = array of TObject;

...

procedure DeleteAt(var AArray : TObjectArray; AIndex : integer);
begin
  while(AIndex < Length(AArray) - 1) do
  begin
    AArray[AIndex] := AArray[AIndex + 1];
    Inc(AIndex);
  end;
  SetLength(AArray, Length(AArray) - 1);
end;

For reusing the same functions, I'm casting several dynamic array of TObject's descendant classes to TObjectArray before passing them to my functions, for example:

var
  Buttons : array of TButton;
begin
  SetLength(Buttons, 1);
  Buttons[0] := Button1;

  //...

  DeleteAt(TObjectArray(Buttons), 0);
end;

Is this a safe practice or it could cause some problem that I didn't considered?

Upvotes: 1

Views: 264

Answers (1)

Disillusioned
Disillusioned

Reputation: 14832

The technique itself doesn't cause harm provided the arrays are compatible. The members of an array of TObject are simply references to objects. Since objects can be used safely from an ancestor reference, there is no inherent problem.

Certainly the code you've shown in your question is safe. You could even Free the object instance you're removing/deleting if that makes sense in terms of your ownership pattern.


That said, there is a risk in that the compiler is unable to perform any of its typical type safety checks.

  • In the extreme case, you're able to cast between for example array of Byte and array of TObject
  • More subtly, you can accidentally cast between arrays of incompatible object types (e.g. cast between array of TButton and array of TQuery). When working with individual objects you have the option of a checked type-cast using (<object> as <type>). This performs a compile-time check to verify the types are within the same hierarchy (otherwise cast is guaranteed to be incompatible), and a run-time check to ensure the object instance is actually of the correct type.
  • Although removing and swapping objects in the array is safe, the var reference means you can also add objects to the array. Now whereas an array of TButton forces you to add TButton (or subclass) instances; having typecast to array of TObject means you can accidentally add any other object to the array. And when the function returns, code will attempt use objects very incorrectly. You're almost certain to get strange behaviour and/or access violations. They will be much more difficult to debug due to the root problem having been silently introduced earlier in the program.

Conclusion

While the technique works, you'd be much better advised to use a TObjectList.

  • This retains the standard object based type-checking.
  • It supports similar use to an array, so is usually a great substitute.
  • And as an extra bonus, the container is able dynamically resize as you add and remove items - no more manual resizing required.
  • The only minor inconvenience is that you have to create and destroy the TObjectList.

Even TList would be a generally better choice, though you'd have to perform unchecked casting between Pointer and class types.

Upvotes: 3

Related Questions