Delphi 为什么要用两个别名来表示;字符串数组";区别对待?

Delphi 为什么要用两个别名来表示;字符串数组";区别对待?,delphi,generics,Delphi,Generics,在Pascal中,有两种类型声明: 类型别名:类型NewName=OldType 类型创建:类型NewType=typeOldType 前者只是创建方便的速记,如C中的typedef。别名彼此兼容,并且与原始类型兼容。创建的类型是故意不兼容的,如果没有显式和不安全的按定义类型转换,则不能混合 var nn: NewName; nt: NewType; ot: OldType; ... nn := ot; // should work nt := ot; // should bre

在Pascal中,有两种类型声明:

  • 类型别名:类型NewName=OldType
  • 类型创建:类型NewType=typeOldType
前者只是创建方便的速记,如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.
这些是我理解的帕斯卡基础知识

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

  • System.Types.TStringDynArray=字符串的数组
  • System.TArray=T的数组;
    • 特别是,这意味着TArray=字符串的数组;根据定义
现在,让我们以返回前一个类型别名的函数为例,将其结果提供给期望后一个类型别名的函数:

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') ) );
同样的,没有解释

PPS。现在有许多文档引用。我想强调一件事:虽然这一限制可能间接继承自1949年的帕斯卡报告,但今天是2012年,德尔福的使用方式与半个世纪前的学校实验室截然不同。 我列举了保持这种限制的一些不良影响,但没有看到任何好的影响

具有讽刺意味的是,这种限制可以在不违反Pascal规则的情况下解除:Pascal中没有开放数组和动态数组这样的非严格野兽。所以,让那些原始的固定数组按照他们的意愿受到限制,但开放数组和动态数组不是Pascal公民,也不必受其代码本的限制


请在QC或甚至在这里与Emba交流,但如果你只是路过而没有表达你的意见-什么都不会改变

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

当您编写数组时,您定义的是一个类型而不是别名。正如David在评论中所说,这两种相同的类型

type 
  T1 = array of string; 
  T2 = array of string;
是不兼容的

同样的道理也适用于

type
  TStringDynArray = array of string;
  TArray<T> = array of string;
类型
TStringDynArray=字符串数组;
TArray=字符串数组;

人们常常忘记了相同类型的不兼容性,我猜他们在引入IOUtils时就忘记了。理论上,TStringDynArray的定义应该更改为
TStringDynArray=TArray
,但我想这可能会引发其他问题(不是泛型的bug…。

理解这个问题的关键是语言指南中的主题。我建议你好好读一下这个话题

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

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.
从文件中:

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

这意味着
TInteger1
TInteger2
是相同的类型,实际上与
Integer
是相同的类型

文档中进一步介绍的是:

作为类型名的语言结构每次出现时都表示不同的类型

TArray1
TArray2
的声明属于此类。这意味着这两个标识符表示不同的类型

现在我们需要看一下讨论兼容性的部分。这提供了一组规则来确定这两种类型是兼容的还是赋值兼容的。事实上,我们可以通过参考另一个帮助主题来缩短讨论的时间:该主题明确指出:

数组只有在类型相同时才与赋值兼容

这就清楚了为什么赋值
Array1:=Array2
会导致编译器错误

您的代码关注传递参数,而我的代码关注赋值。这些问题是相同的,因为正如帮助主题所解释的:

调用例程时,请记住:

  • 用于传递类型化常量和值参数的表达式必须与相应的形式参数赋值兼容

我在Delphi中也遇到了同样的问题,我想将值从一个相同的数组传递到另一个数组。我不仅在两个类似的数组赋值中遇到了“不兼容”问题,而且还无法使用“Copy()”过程。为了解决这个问题,我发现可以使用一个指向字符串数组类型数组的指针

例如:

type RecArry = array of array of string
     end;
var TArryPtr : ^RecArry;
现在,我可以将值从任何固定数组传递到另一个相同的数组,而不存在任何兼容性或函数问题。例如:

TArryPtr := @RecArry.LstArray //This works!
TArryPtr := @LstArray         //This also works!
Two_Dimensional_Fixed_Array[10][0]
有了这个创建的数组分配模板,我现在可以毫无问题地处理所有二维数组。但是,应该理解的是,当访问这种类型的字符串数组指针时,会创建一个额外的元素,因此当我们在下面期望这种类型的数组时,例如:

TArryPtr := @RecArry.LstArray //This works!
TArryPtr := @LstArray         //This also works!
Two_Dimensional_Fixed_Array[10][0]
现在我们得到了一个额外的元素调整数组,如下所示:

New_Two_Dimensional_Fixed_Array[10][1]    
这意味着我们必须使用一些稍微复杂的代码来访问指针数组,因为二维固定数组[10][0]中所有填充的元素都向下移动,因此它们被1偏移,就像在新的二维固定数组[10][1]中一样

因此,我们通常在二维固定数组[1][0]中找到值“X”,现在可以在TArryPtr[0][1]中找到它

这是一种我们都必须接受的交易

要记住的另一个重要注意事项是在声明指针数组时定义它。当指针数组声明为类型时,Borland编译器将不允许指针数组具有与数组t相同的元素大小