使用通用接口的错误重载调用
Wrong overloaded call using generic interfaces
假设我有这样的定义:
TMyClass1 = class
end;
TMyClass2 = class
end;
IModel<T : class> = interface
['{E8262D6C-DCAB-46AC-822E-EC369CF734F8}']
function List() : TObjectList<T>;
end;
IPresenter<T : class> = interface
['{98FB7751-D75A-4C51-B55A-0E5FE68BE213}']
function Retrieve() : TObjectList<T>;
end;
IView<T : class> = interface
['{59384CD6-30D6-4BD8-AB3D-7FCF4D1A8618}']
procedure AssignPresenter(APresenter : IPresenter<T>);
end;
TModel<T : class> = class(TInterfacedObject, IModel<T>)
public
function List() : TObjectList<T>; virtual; abstract;
end;
TPresenter<T : class> = class(TInterfacedObject, IPresenter<T>)
strict private
{ Private declarations }
FModel : IModel<T>;
FView : IView<T>;
public
constructor Create(AView : IView<T>);
function Retrieve() : TObjectList<T>; virtual; abstract;
end;
TModelClass1 = class(TModel<TMyClass1>);
TPresenterClass1 = class(TPresenter<TMyClass1>);
TModelClass2 = class(TModel<TMyClass2>);
TPresenterClass2 = class(TPresenter<TMyClass2>);
我有这个表单来实现我定义的一些东西:
TForm1 = class(TForm, IView<TMyClass1>, IView<TMyClass2>)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FPresenter1 : IPresenter<TMyClass1>;
FPresenter2 : IPresenter<TMyClass2>;
procedure AssignPresenter(APresenter : IPresenter<TMyClass1>); overload;
procedure AssignPresenter(APresenter : IPresenter<TMyClass2>); overload;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
TPresenterClass1.Create((Self as IView<TMyClass1>));
TPresenterClass2.Create((Self as IView<TMyClass2>));
end;
procedure TForm1.AssignPresenter(APresenter: IPresenter<TMyClass1>);
begin
Self.FPresenter1 := APresenter;
end;
procedure TForm1.AssignPresenter(APresenter: IPresenter<TMyClass2>);
begin
Self.FPresenter2 := APresenter;
end;
所以这里的问题是 delphi 无法弄清楚要调用什么方法,在这个例子中,在这两种情况下只调用了 AssignPresenter(APresenter: IPresenter<TMyClass2>)
,所以我可能在这里遗漏了一些东西但是我不知道 atm。
提前致谢。
这可能是重复的,但我现在找不到它。
问题是接口的 as
运算符与泛型不是很兼容。 as
运算符依赖于接口 GUID。该接口是通过查询具有匹配 GUID 的接口来找到的。而且 GUID 根本不适合泛型实例化。
现在让我们看看您的代码。
TPresenterClass1.Create((Self as IView<TMyClass1>));
TPresenterClass2.Create((Self as IView<TMyClass2>));
问题是 IView<TMyClass1>
和 IView<TMyClass2>
具有相同的 GUID:
type
IView<T : class> = interface
['{59384CD6-30D6-4BD8-AB3D-7FCF4D1A8618}']
procedure AssignPresenter(APresenter : IPresenter<T>);
end;
所以IView<TMyClass1>
和IView<TMyClass2>
共享相同的GUID,当你使用as
查询时,无论是否请求[=]都会返回相同的界面14=] 或 IView<TMyClass2>
.
所以,这里的底线是,一旦对象使用不同的 T
实现了 ISomeInterface<T>
两次,as
就几乎无法使用通用接口。
Embarcadero 确实应该以支持泛型的方式实现 as
。不过我不会屏住呼吸。
您需要找到其他方法来解决您的问题。
作为对 David Heffernan 回答的补充。
解决该问题的一种方法是为您使用的所有通用声明提供唯一的 GUID:
IViewMyclass1 = interface(IView<TMyClass1>)
['{1A0F941F-BAB1-4723-A6C1-27036DF5D344}']
end;
IViewMyclass2 = interface(IView<TMyClass2>)
['{0C61A23A-DC50-43B0-97C9-8B0013DDC193}']
end;
重新定义视图的声明。
TForm4 = class(TForm, IViewMyclass1, IViewMyclass2)
procedure TForm1.FormCreate(Sender: TObject);
begin
TPresenterClass1.Create((Self as IViewMyclass1));
TPresenterClass2.Create((Self as IViewMyclass2));
end;
然后调用正确的重载。
免责声明:
- 这是即时测试 (XE4) 的结果。可靠性未知。
- 我知道这不太实用。
- 它可能不是应该广泛使用的东西,因为代码会在哪里失败并不明显。
当你有实现接口的对象时不需要使用as
- 只需写:
procedure TForm1.FormCreate(Sender: TObject);
begin
TPresenterClass1.Create(Self);
TPresenterClass2.Create(Self);
end;
编译器正确地解决了这个问题。当您使用 as
时,它会执行 Supports
调用,但由于 David 已经解释过的原因而失败。
假设我有这样的定义:
TMyClass1 = class
end;
TMyClass2 = class
end;
IModel<T : class> = interface
['{E8262D6C-DCAB-46AC-822E-EC369CF734F8}']
function List() : TObjectList<T>;
end;
IPresenter<T : class> = interface
['{98FB7751-D75A-4C51-B55A-0E5FE68BE213}']
function Retrieve() : TObjectList<T>;
end;
IView<T : class> = interface
['{59384CD6-30D6-4BD8-AB3D-7FCF4D1A8618}']
procedure AssignPresenter(APresenter : IPresenter<T>);
end;
TModel<T : class> = class(TInterfacedObject, IModel<T>)
public
function List() : TObjectList<T>; virtual; abstract;
end;
TPresenter<T : class> = class(TInterfacedObject, IPresenter<T>)
strict private
{ Private declarations }
FModel : IModel<T>;
FView : IView<T>;
public
constructor Create(AView : IView<T>);
function Retrieve() : TObjectList<T>; virtual; abstract;
end;
TModelClass1 = class(TModel<TMyClass1>);
TPresenterClass1 = class(TPresenter<TMyClass1>);
TModelClass2 = class(TModel<TMyClass2>);
TPresenterClass2 = class(TPresenter<TMyClass2>);
我有这个表单来实现我定义的一些东西:
TForm1 = class(TForm, IView<TMyClass1>, IView<TMyClass2>)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FPresenter1 : IPresenter<TMyClass1>;
FPresenter2 : IPresenter<TMyClass2>;
procedure AssignPresenter(APresenter : IPresenter<TMyClass1>); overload;
procedure AssignPresenter(APresenter : IPresenter<TMyClass2>); overload;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
TPresenterClass1.Create((Self as IView<TMyClass1>));
TPresenterClass2.Create((Self as IView<TMyClass2>));
end;
procedure TForm1.AssignPresenter(APresenter: IPresenter<TMyClass1>);
begin
Self.FPresenter1 := APresenter;
end;
procedure TForm1.AssignPresenter(APresenter: IPresenter<TMyClass2>);
begin
Self.FPresenter2 := APresenter;
end;
所以这里的问题是 delphi 无法弄清楚要调用什么方法,在这个例子中,在这两种情况下只调用了 AssignPresenter(APresenter: IPresenter<TMyClass2>)
,所以我可能在这里遗漏了一些东西但是我不知道 atm。
提前致谢。
这可能是重复的,但我现在找不到它。
问题是接口的 as
运算符与泛型不是很兼容。 as
运算符依赖于接口 GUID。该接口是通过查询具有匹配 GUID 的接口来找到的。而且 GUID 根本不适合泛型实例化。
现在让我们看看您的代码。
TPresenterClass1.Create((Self as IView<TMyClass1>));
TPresenterClass2.Create((Self as IView<TMyClass2>));
问题是 IView<TMyClass1>
和 IView<TMyClass2>
具有相同的 GUID:
type
IView<T : class> = interface
['{59384CD6-30D6-4BD8-AB3D-7FCF4D1A8618}']
procedure AssignPresenter(APresenter : IPresenter<T>);
end;
所以IView<TMyClass1>
和IView<TMyClass2>
共享相同的GUID,当你使用as
查询时,无论是否请求[=]都会返回相同的界面14=] 或 IView<TMyClass2>
.
所以,这里的底线是,一旦对象使用不同的 T
实现了 ISomeInterface<T>
两次,as
就几乎无法使用通用接口。
Embarcadero 确实应该以支持泛型的方式实现 as
。不过我不会屏住呼吸。
您需要找到其他方法来解决您的问题。
作为对 David Heffernan 回答的补充。
解决该问题的一种方法是为您使用的所有通用声明提供唯一的 GUID:
IViewMyclass1 = interface(IView<TMyClass1>)
['{1A0F941F-BAB1-4723-A6C1-27036DF5D344}']
end;
IViewMyclass2 = interface(IView<TMyClass2>)
['{0C61A23A-DC50-43B0-97C9-8B0013DDC193}']
end;
重新定义视图的声明。
TForm4 = class(TForm, IViewMyclass1, IViewMyclass2)
procedure TForm1.FormCreate(Sender: TObject);
begin
TPresenterClass1.Create((Self as IViewMyclass1));
TPresenterClass2.Create((Self as IViewMyclass2));
end;
然后调用正确的重载。
免责声明:
- 这是即时测试 (XE4) 的结果。可靠性未知。
- 我知道这不太实用。
- 它可能不是应该广泛使用的东西,因为代码会在哪里失败并不明显。
当你有实现接口的对象时不需要使用as
- 只需写:
procedure TForm1.FormCreate(Sender: TObject);
begin
TPresenterClass1.Create(Self);
TPresenterClass2.Create(Self);
end;
编译器正确地解决了这个问题。当您使用 as
时,它会执行 Supports
调用,但由于 David 已经解释过的原因而失败。