Tiago
Tiago

Reputation: 183

Delphi code migration issues

I´m facing a problem between Delphi 2010 and Delphi Berlin (last update) during my code migration.... I made a simple code to demonstrante an strange behaviour...

I have an application that use TList (the former one) and TList (from Generics.Collections) I know that this piece of code (below) doesn´t make any sense for you, but it´s for demonstration purposes

unit Unit1;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
  TTest = class
    Name: string;
    constructor Create(Nome: string);
  end;
  TForm1 = class(TForm)
    btn1: TButton;
    procedure btn1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    FList: TList;
  end;
var
  Form1: TForm1;
implementation
uses
  System.Generics.Collections;
{$R *.dfm}

procedure TForm1.btn1Click(Sender: TObject);
var
  tmpList: TList<TTest>;
begin
  tmpList := TList<TTest>.Create;
  tmpList.Add(TTest.Create('A'));
  tmpList.Add(TTest.Create('B'));
  tmpList.Add(TTest.Create('C'));
  tmpList.Add(TTest.Create('D'));
  tmpList.Add(TTest.Create('E'));
  FList := TList(tmpList);
  ShowMessage(TTest(FList[0]).Name);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FList := TList.Create;
end;

constructor TTest.Create(Nome: string);
begin
  Name := Nome;
end;
end.

At Delphi 2010 the ShowMessage shows 'A' character, but on the Delphi Berlin it raises an Acess Violation

Both applications with Optimization set to False

Upvotes: 5

Views: 189

Answers (2)

David Heffernan
David Heffernan

Reputation: 612794

FList := TList(tmpList);

This is the problem. The cast is simply wrong, because tmpList is not a TList.

Your code only compiles because of the cast, but the cast does not change the fact that the object on the right hand side is not of the type being casted to. All the cast does is stop the compiler from complaining and saving you from yourself. Your cast is a lie to the compiler, and the runtime error is the consequence.

This code might have worked in older versions, but only by chance. Your luck has changed.

Hard to know what to suggest for a fix. As you say, the code makes little sense. Every time you press the button, you leak a list. I'd suggest that you remove all the casts, stop using the non-Generic TList and use only Generic lists.

Upvotes: 9

Deltics
Deltics

Reputation: 23036

The class TList<T> is not castable to/from TList.

You cannot cast one to the other and expect sensible results any more than you could cast a TForm to TButton (for example).

In Delphi, typecasts of this form are unchecked, sometimes referred to as hard-casting. That is, the compiler will simply trust that you know what you are doing and will simply comply, but if the typecast is invalid then the results will be unpredictable.

For conversions between object reference types (and/or interface references) you can use a checked cast using the as operator:

FList := tmpList as TList;

If a checked cast is invalid (such as this one is) then the compiler will throw a runtime exception, alerting you to the mistake.

Why does the compiler even allow unchecked casts ?

In some cases unchecked casting can be useful and safely relied upon, within specific use cases. But outside of those specific conditions unchecked casts are at best trusting to luck or on specific compiler behaviours or RTL characteristics which may be subject to change.

e.g. the 32-bit trick of storing object references or other pointer values in an Integer variable. Such code may continue to work when recompiled for 64-bit, but now only as a matter of luck and only in some cases, since only a subset of possible 64-bit pointer values can safely be stored in a 32-bit Integer.

If you have code which is successfully hard-casting between TList and TList<T> then it worked only by luck, as a result of some particular behaviour of the compiler or RTL at that time.

Upvotes: 6

Related Questions