我在代码迁移期间遇到了Delphi 2010和Delphi Berlin(上次更新)之间的问题.... 我做了一个简单的代码来证明一个奇怪的行为...
我有一个使用TList(前一个)和TList(来自Generics.Collections)的应用程序 我知道这段代码(下面)对你没有任何意义,但它是出于演示目的
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.
在Delphi 2010中,ShowMessage显示'A'字符,但在Delphi Berlin上它会引发Acess Violation
优化的两个应用程序都设置为False
答案 0 :(得分:9)
FList := TList(tmpList);
这就是问题所在。演员阵容完全错误,因为tmpList
不是TList
。
您的代码仅因为强制转换而编译,但强制转换不会改变右侧对象不属于要转换的类型的事实。所有演员都会阻止编译器抱怨并将你从自己身上拯救出来。你的演员阵容是编译器的谎言,结果是运行时错误。
此代码可能适用于旧版本,但只是偶然。你的运气发生了变化。
很难知道修复建议的内容。正如你所说,代码没有多大意义。每次按下按钮,都会泄漏列表。我建议您删除所有演员表,停止使用非Generic TList
并仅使用Generic列表。
答案 1 :(得分:6)
课程TList<T>
不能投放到TList
。
你不能将一个投射到另一个并且期望得到明显的结果,而不是将TForm
投射到TButton
(例如)。
在Delphi中,此表单的类型转换未经检查,有时称为 hard-casting 。也就是说,编译器只会相信您知道自己在做什么并且只是遵守,但如果类型转换无效,那么结果将是不可预测的。
对于对象引用类型(和/或接口引用)之间的转换,您可以使用作为运算符使用已检查强制转换:
FList := tmpList as TList;
如果选中的强制转换无效(例如这个),则编译器将抛出运行时异常,提醒您错误。
在某些情况下,在特定用例中,未经检查的铸造可能是有用且安全的依赖。但是在这些特定条件之外,未经检查的强制转换最多只能信赖运气或特定的编译器行为或RTL特性,这些特性可能会发生变化。
e.g。在 Integer 变量中存储对象引用或其他指针值的32位技巧。这样的代码可能在重新编译为64位时继续工作,但现在只是为了运气而且仅在某些情况下,因为只有一部分可能的64位指针值可以安全地存储在一个32位的整数。
如果您的代码在TList
和TList<T>
之间成功地进行了强制转换,那么由于当时编译器或RTL的某些特定行为,它只能运气好。