为什么两个别名的"字符串数组"处理不同?

Ari*_*The 12 delphi generics

在Pascal中有两种类型声明:

  • 类型别名:类型NewName = OldType
  • 类型创建:类型NewType = type OldType

前者只是创建方便的速记,如C中的typedef.别名是彼此兼容的,与原始类型兼容.创建的类型是故意不兼容的,并且在没有明确和不安全的情况下通过类型转换不能混合.

var
  nn: NewName; nt: NewType; ot: OldType;
...
  nn := ot; // should work
  nt := ot; // should break with type safety violation error.

  nt := NewType(ot); // Disabling type safety. Should work even if 
  // it has no sense semantically and types really ARE incompatible.
Run Code Online (Sandbox Code Playgroud)

根据我的理解,这些是Pascal基础知识.

现在让我们看一个特定类型和两个别名:

  • System.Types.TStringDynArray = 字符串数组 ;
  • System.TArray <T> = T的数组 ;
    • 特别是这意味着 TArray <string> = 字符串数组 ; 根据定义.

现在让我们把函数返回前一个类型别名并将其结果提供给期望后者的函数:

uses Classes, IOUtils;

 TStringList.Create.AddStrings(
    TDirectory.GetFiles('c:\', '*.dll') );

 TStringList.Create.AddStrings(
     TArray<string>( // this is required by compiler - but why ???
         TDirectory.GetFiles('c:\', '*.dll') ) );
Run Code Online (Sandbox Code Playgroud)

由于类型违规,第一个代码段无法编译.第2个愉快地编译和工作,但是对未来的类型变化是脆弱的并且是多余的.

QC告诉编译器是正确的,RTL设计是错误的. http://qc.embarcadero.com/wc/qcmain.aspx?d=106246

为什么编译器就在这里?为什么这些别名不兼容?甚至RTL的设计方式也表明它们被认为是兼容的!

PS.大卫提出了更简单的例子,没有使用TArray <T>

 type T1 = array of string; T2 = array of string;

 procedure TForm1.FormCreate(Sender: TObject);
  function Generator: T1;
    begin Result := T1.Create('xxx', 'yyy', 'zzz'); end;
  procedure Consumer (const data: T2);
    begin
      with TStringList.Create do 
      try
        AddStrings(data);
        Self.Caption := CommaText;
      finally
        Free;
      end;
    end;
  begin
    Consumer(Generator);
  end;
Run Code Online (Sandbox Code Playgroud)

相同的问题没有解释......

PPS.现在有很多doc refs.我想强调一件事:虽然这种限制可能间接地继承自1949年的Pascal报告,但今天是2012年,德尔福与半个世纪前的学校实验室的使用方式截然不同.我点了几个BAD效果来保持这个限制,但没有看到任何好的.

讽刺的是,这种限制可能会在不破坏Pascal规则的情况下解除:在Pascal中没有像Open Arrays和Dynamic Arrays这样的非严格的野兽.因此,让那些原始的固定数组按照他们的意愿进行限制,但Open Arrays和Dynamic Arrays不是Pascal公民,也没有义务受其代码簿的限制!

请在QC或甚至在这里与Emba联系,但如果你只是在没有表达意见的情况下过去 - 没有什么会改变!

Dav*_*nan 11

理解此问题的关键是语言指南中的类型兼容性和标识主题.我建议你对这个话题有一个很好的解读.

简化示例也很有帮助.在示例中包含泛型主要是为了使问题复杂化和混淆.

program TypeCompatibilityAndIdentity;
{$APPTYPE CONSOLE}

type
  TInteger1 = Integer;
  TInteger2 = Integer;
  TArray1 = array of Integer;
  TArray2 = array of Integer;
  TArray3 = TArray1;

var
  Integer1: TInteger1;
  Integer2: TInteger2;
  Array1: TArray1;
  Array2: TArray2;
  Array3: TArray3;

begin
  Integer1 := Integer2; // no error here
  Array1 := Array2; // E2010 Incompatible types: 'TArray1' and 'TArray2'
  Array1 := Array3; // no error here
end.
Run Code Online (Sandbox Code Playgroud)

从文档:

当使用另一个类型标识符声明一个类型标识符时,它们没有限定条件,它们表示相同的类型.

这意味着,TInteger1TInteger2同一类型,的确是同一类型Integer.

在文档中稍微进一步说明:

用作类型名称的语言结构在每次出现时表示不同的类型.

宣言TArray1TArray2属于这一类.这意味着这两个标识符表示不同的类型.

现在我们需要看一下讨论兼容性的部分.这给出了一组规则,以确定两种类型是兼容的还是赋值兼容的.事实上,我们可以通过引用另一个帮助主题来快速讨论该讨论:结构化类型,数组类型和赋值清楚地说明:

只有当数组属于同一类型时,它们才是赋值兼容的.

这清楚地说明了赋值Array1 := Array2导致编译器错误的原因.

您的代码查看了传递参数,但我的重点是分配.问题是相同的,因为调用过程和函数帮助主题解释:

在调用例程时,请记住:

  • 用于传递类型const和值参数的表达式必须与相应的形式参数分配兼容.
  • .......

  • *将参数传递给过程几乎是*是的,就像Delphi中的编程与汇编程序中的编程一样.所有图灵完备语言都只是彼此的语法糖.但实际上我并没有使用直接的非变异任务.它只是没有实际意义.但是我确实将一个函数称为另一个函数.如果编译器将其视为assignin - 就是它,它就是它的内部选择.我关心兼容性=表达式和函数的可组合性. (2认同)
  • 是的,我昨天浏览了1949/1974的"帕斯卡报告",这条规则适合那里.没有像"打开数组"这样的东西,你不能使`procedure xxx(list yy:byte of array)`; 一切都必须有名字.而且你只是为你的学校工作提供了类似冒泡的小片段,没有别的代码,现在你自己的代码来自昨天,所以是的,你可以计划AL的数据类型.在1949年你可以.但今天它只是不一致. (2认同)

Ste*_*nke 7

Delphi是一种强类型语言.这意味着相同(在这种情况下,我的意思是它们的定义看起来完全相同)类型不是赋值兼容的.

在编写时,array of <type>您定义的是类型而不是别名.正如大卫在评论中已经说过两个相同的类型

type 
  T1 = array of string; 
  T2 = array of string;
Run Code Online (Sandbox Code Playgroud)

不兼容分配.

同样如此

type
  TStringDynArray = array of string;
  TArray<T> = array of string;
Run Code Online (Sandbox Code Playgroud)

通常人们会忘记相同类型的不兼容性,我的猜测是他们在介绍IOUtils时所做的.从理论上讲,TStringDynArray的定义应该已经改为TStringDynArray = TArray<string>但我想这可能会引发其他问题(不是说泛型的错误......).