【问题标题】:Delphi code migration issuesDelphi 代码迁移问题
【发布时间】:2017-07-19 18:26:07
【问题描述】:

在我的代码迁移过程中,我遇到了 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 上却引发了访问冲突

优化设置为 False 的两个应用程序

【问题讨论】:

    标签: delphi


    【解决方案1】:
    FList := TList(tmpList);
    

    这就是问题所在。演员阵容完全是错误的,因为tmpList 不是TList

    您的代码仅因为强制转换而编译,但强制转换不会改变右侧的对象不是被强制转换的类型的事实。演员所做的一切就是阻止编译器抱怨并将你从自己手中拯救出来。您的演员表是对编译器的谎言,结果是运行时错误。

    此代码可能在旧版本中有效,但只是偶然。你的运气变了。

    很难知道有什么建议可以修复。正如您所说,代码毫无意义。每次按下按钮,都会泄露一个列表。我建议您删除所有演员表,停止使用非通用 TList 并仅使用通用列表。

    【讨论】:

      【解决方案2】:

      TList&lt;T&gt; 类不能转换为 TList 或从 TList 转换。

      您不能将一个TForm 转换为TButton(例如)。

      在 Delphi 中,这种形式的类型转换是未经检查的,有时称为 hard-casting。也就是说,编译器只会相信你知道自己在做什么,并且会简单地遵守,但如果类型转换无效,那么结果将是不可预测的。

      对于对象引用类型(和/或接口引用)之间的转换,您可以使用 as 运算符进行 checked 类型转换:

      FList := tmpList as TList;
      

      如果检查强制转换无效(例如这个),那么编译器将抛出运行时异常,提醒您注意错误。

      为什么编译器甚至允许未经检查的强制转换?

      在某些情况下,在特定用例中,未经检查的强制转换可能有用且安全可靠。但在这些特定条件之外,未经检查的强制转换充其量只能依靠运气或特定的编译器行为或可能会发生变化的 RTL 特性。

      例如在 Integer 变量中存储 object references 或其他 pointer 值的 32 位技巧。此类代码可能在重新编译为 64 位时继续工作,但现在只是运气好,并且仅在某些情况下,因为只有可能的 64 位指针值的子集可以安全地存储在一个 32 位整数。

      如果您的代码在 TListTList&lt;T&gt; 之间成功硬转换,那么它只能靠运气,因为当时编译器或 RTL 的某些特定行为。

      【讨论】:

      • 标签是指针大小的,所以倒数第二个段落放错了位置。它不是整数。它是 NativeInt。
      • 不,倒数第二段是 100% 准确的。问题在于早期不恰当地引用 Tag 作为使用 Integer 存储指针的不正确(实际上是不必要的)示例。我只是删除了不正确的示例。
      • 现在您删除了对 Tag 的错误引用是准确的。事实上,这将是一个很好的例子,说明硬演员何时是合理的。指针和 NativeInt 之间的转换。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-11
      • 1970-01-01
      • 2011-12-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多