在Pascal中有两种类型声明:
前者只是创建方便的速记,如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.
这些是我理解的Pascal基础知识。
现在让我们看一个特定类型和两个别名:
现在我们让函数返回前一个类型的别名,并将其结果提供给期望后者的函数:
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') ) );
由于类型违规,第一个代码段无法编译。 第二个愉快地编译和工作,但是对于未来的类型变化是脆弱的并且是多余的。
QC告诉编译器是正确的,RTL设计是错误的。 http://qc.embarcadero.com/wc/qcmain.aspx?d=106246
为什么编译器就在这里? 为什么这些别名不兼容? 甚至RTL的设计方式也表明它们被认为是兼容的!
PS。大卫提出了更简单的例子,没有使用TArray&lt; T&gt;
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;
相同的问题没有解释......
PPS。现在有很多doc refs。我想强调一件事:虽然这种限制可能间接地继承自1949年的Pascal报告,但今天是2012年,德尔福与半个世纪前的学校实验室的使用方式截然不同。 我点了几个BAD效果来保持这个限制,但没有看到任何好的。
讽刺的是,这种限制可能会在不破坏Pascal规则的情况下被解除:在Pascal中没有像Open Arrays和Dynamic Arrays这样的非严格的野兽。所以让那些原始的固定数组按照他们的意愿限制,但Open Arrays和Dynamic Arrays不是Pascal公民,也没有义务受其代码簿的限制!
请在QC或甚至在这里与Emba联系,但如果你只是在没有表达意见的情况下过去 - 没有什么会改变的!
答案 0 :(得分:11)
理解此问题的关键是语言指南中的Type Compatibility and Identity主题。我建议你对这个话题有一个很好的解读。
简化示例也很有帮助。在示例中包含泛型主要是为了使问题复杂化和混淆。
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
的声明属于此类别。这意味着这两个标识符表示不同的类型。
现在我们需要查看讨论兼容性的部分。这给出了一组规则,以确定两种类型是兼容的还是赋值兼容的。事实上,我们可以通过引用另一个帮助主题来简化讨论:Structured Types, Array Types and Assignments明确指出:
只有当数组属于同一类型时,数组才是赋值兼容的。
这清楚地说明了赋值Array1 := Array2
导致编译器错误的原因。
您的代码查看了传递参数,但我的重点是分配。问题是相同的,因为Calling Procedures and Functions帮助主题解释了:
调用例程时,请记住:
- 用于传递类型const和值参数的表达式必须与相应的形式参数分配兼容。
- .......
答案 1 :(得分:7)
Delphi是一种强类型语言。这意味着相同(在这种情况下,我的意思是它们的定义看起来完全相同)类型不是赋值兼容的。
当您编写array of <type>
时,您正在定义类型而不是别名。正如大卫在评论中已经说过两个相同的类型,如
type
T1 = array of string;
T2 = array of string;
不兼容作业。
同样适用于
type
TStringDynArray = array of string;
TArray<T> = array of string;
通常人们会忘记相同类型的不兼容性,我猜他们会在他们引入IOUtils时做到这一点。从理论上讲,TStringDynArray的定义应该已经改为TStringDynArray = TArray<string>
,但我想这可能会引发其他问题(不是说泛型错误......)。
答案 2 :(得分:2)
我也遇到了与Delphi相同的问题,我希望将值从一个相同的数组传递到另一个数组。我不仅有两个类似数组赋值的“不兼容”问题,而且我也无法使用“Copy()”过程。为了解决这个问题,我发现我可以使用指向 type 数组字符串数组的指针。
例如:
type RecArry = array of array of string
end;
var TArryPtr : ^RecArry;
现在,我可以将任何固定数组中的值传递给另一个相同的数组,而不会出现任何兼容性或功能问题。例如:
TArryPtr := @RecArry.LstArray //This works!
TArryPtr := @LstArray //This also works!
使用这个创建的数组赋值模板,我现在可以使用所有二维数组而不会出现任何问题。但是,应该理解,当访问这种类型的字符串数组指针时,会创建一个额外的元素,这样当我们期望下面这种类型的数组2D数组时,例如:
Two_Dimensional_Fixed_Array[10][0]
我们现在得到一个额外的元素调整数组,如下所示:
New_Two_Dimensional_Fixed_Array[10][1]
这意味着我们必须使用一些稍微棘手的代码来访问指针数组,因为Two_Dimensional_Fixed_Array [10] [0]中的所有填充元素都向下移动,因此它们偏移1,如New_Two_Dimensional_Fixed_Array [10] ] [1]。
因此,我们通常会在Two_Dimensional_Fixed_Array [1] [0] 中找到值'X',现在可以在TArryPtr [0]中找到它] [1] 的
这是我们所有人必须忍受的权衡!
要记住的另一个重要注意事项是声明指针数组时的定义。当指针数组是声明的类型时,Borland编译器将不允许指针数组具有与其指向的数组相同的元素大小。例如,如果数组声明为:
Orig_Arry : array [1..50,1] of string;
指向它的指针数组将以下列方式声明:
Type Pntr_Arry : array [1..50,2] of string;
你注意到了额外的元素吗?我猜测Borland编译器必须扩展数组指针以允许指针地址。