0

有谁知道为什么指示的行有错误?

根据类型兼容性页面,这似乎是一个编译器错误。通配符是 T1,TStringWildcard2 是 T2。

也根据这个

如果表达式的值在 T1 的范围内并且至少满足以下条件之一,则可以将 T2 类型的表达式分配给 T1 类型的变量:T1 是 IUnknown 或 IDispatch 接口类型,并且 T2 是 Variant 或OleVariant。(如果 T1 是 IUnknown,则变体的类型代码必须是 varEmpty、varUnknown 或 varDispatch,如果 T1 是 IDispatch,则必须是 varEmpty 或 varDispatch。)

如果 T1 是 IUnknown 并且 T2 是 OleVariant,则可以将 T2 类型的表达式分配给 T1 类型的变量 - 因此第二个错误行也应该编译。

ACollection2 := ACollection1; // 理论上应该编译,但没有。由于 LWildcard := TStringWildcard2.Create (编译,因为 LWildcard 被声明为 Wildcard 接口的变量,并且 TStringWildcard2 实现了 Wildcard 接口)。

As such, therefore, ACollection2 (TDictionary<string, Wildcard> ) := ACollection1; (TDictionary<string, TStringWildcard2>) should compile right?

program TestCollections;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils, System.Generics.Collections;
type
  Wildcard = interface
  ['{941128E5-D87C-4E3E-98D8-CF45EE6FEC09}']
    procedure Clear;
  end;
  Wildcard<T> = interface(Wildcard)
    function  getMatch: T;
  end;
  TParent = class(TInterfacedObject)
  end;
  TStringWildcard1 = class(TParent, Wildcard<string>)
    function  getMatch: string;
    procedure Clear;
  end;
  TStringWildcard2 = class(TParent, Wildcard, Wildcard<string>)
    function  getMatch: string;
    procedure Clear;
  end;

{ TStringWildcard1 }

procedure TStringWildcard1.Clear;
begin
end;

function TStringWildcard1.getMatch: string;
begin
  Result := 'String match 1';
end;

{ TStringWildcard2 }

procedure TStringWildcard2.Clear;
begin
end;

function TStringWildcard2.getMatch: string;
begin
  Result := 'String match 2';
end;

var
  LWildcard: Wildcard;
  ACollection1: TDictionary<string, TStringWildcard2>;
  ACollection2, ACollection3: TDictionary<string, Wildcard>;
  ACollection4: TDictionary<string, IUnknown>;
begin
  LWildcard := TStringWildCard2.Create; // <-- TStringWildcard2 compatible with Wildcard
// Since TStringWildcard2 is compatible with Wildcard, therefore, 
// ACollection1 should be compatible with ACollection2
  ACollection1 := TDictionary<string, TStringWildcard2>.Create;
  ACollection2 := ACollection1;
  ACollection4 := TDictionary<string, OleVariant>.Create;
end.
4

2 回答 2

4

这里没有编译器错误。编译器按设计运行。分配失败是因为类型确实不兼容。Delphi 泛型类型是不变的。

文档说:

如果基本类型相同(或者是通用类型的别名)并且类型参数相同,则两个实例化的泛型被认为是赋值兼容的。

现在,让我们看看第一个失败的分配:

var
  ACollection1: TDictionary<string, TStringWildcard2>;
  ACollection2: TDictionary<string, Wildcard>;
....
ACollection2 := ACollection1;

这失败了,因为类型参数不相同。

因为ACollection4我们有

var
  ACollection4: TDictionary<string, IUnknown>;
....
ACollection4 := TDictionary<string, OleVariant>.Create;

同样,类型参数不相同。


语言设计者选择使泛型类型保持不变是有充分理由的。考虑以下示例。

type
  TClass1 = class(TObject)
  end;
  TClass2 = class(TClass1)
  end;

var
  List1: TList<TClass1>;
  List2: TList<TClass2>;
....
List2 := TList<TClass2>.Create;
List1 := List2; // does not compile, but let's imagine that it did
List1.Add(TClass1.Create);

由于List1List2是同一个对象,我们现在已经成功地将一个类型的对象TClass1放入List2其中,从而破坏了类型系统。

事实上你试图分配

ACollection2 := ACollection1;

说明了这个问题。假设该分配是有效的,然后您添加了ACollection2一些已实现Wildcard但未实现的内容TStringWildcard2。然后同样的东西会被添加,ACollection1突然之间你成功地添加了ACollection1一些没有的东西,TStringWildcard2你已经破坏了类型系统。

在支持通用方差的语言中,需要运行时检查来阻止这种情况发生。碰巧的是,就在昨天,我们自己的 Jon Skeet 就这个话题发表了博客:数组协方差:不仅丑陋,而且也很慢。所以,小心你的愿望!


因此,Delphi 设计者选择让 Delphi 泛型类型保持不变。

于 2013-06-23T09:17:45.117 回答
1
于 2013-06-23T07:53:17.673 回答