访问多个实现同名接口的单元
Acessing several units implementing a Interface with equal names
我有两个或多个单元需要从第三方下载,当你们的版本有变化时。
我使用 xml 数据绑定来生成单位。它们是这样的:
unit tissV01;
interface
uses .....;
type
IXMLMensagemTISS = interface(IXMLNode)
['{11773827-F0A1-42E0-99E1-E221DFAF8542}']
{ Property Accessors }
end;
function GetmensagemTISS(Doc: IXMLDocument): IXMLMensagemTISS;
implementation
function GetmensagemTISS(Doc: IXMLDocument): IXMLMensagemTISS;
begin
Result := XXXX as IXMLMensagemTISS;
end;
end.
单位tissV02
unit tissV02;
interface
uses .....;
type
{ IXMLMensagemTISS }
IXMLMensagemTISS = interface(IXMLNode)
['{11773827-F0A1-42E0-99E1-E221DFAF8542}']
{ Property Accessors }
property Cabecalho: string read Get_Cabecalho;
end;
function GetmensagemTISS(Doc: IXMLDocument): IXMLMensagemTISS;
implementation
function GetmensagemTISS(Doc: IXMLDocument): IXMLMensagemTISS;
begin
Result := XXXX as IXMLMensagemTISS;
end;
end.
在我的应用程序中,我需要选择我必须使用的单位:
unit test;
interface
uses tissV01,tissV02, .......;
type
TMyform = class(TForm)
public
msg3:IXMLMensagemTISS;
end;
implementation
procedure TMyform.ExecuteMessage:
var
xmlTISS : TXmlDocument;
begin
xmlTISS := TXmlDocument.Create(nil);
if condition then
msg3 := tissV01.GetmensagemTISS(xmlTISS)
else msg3 := tissV02.GetmensagemTISS(xmlTISS);
with msg3.Cabecalho do something;
end;
end.
从逻辑上讲,它不起作用,因为 IXMLMensagemTISS 对两个单元都是通用的。
是否有一些解决方法可以做到这一点而不必更改接口名称 (IXMLMensagemTISS)?
我想简化我的代码,并且我需要在未来维护很多这种类型的单元。问题是所有人都实现了 IXMLMensagemTISS 而我无法改变它。
我不想创建很多 msg 变量,例如 msgV01:=tissv01.GetmensagemTISS、msgV01:=tissv02.GetmensagemTISS、...等等
如果您在不同的单位中有两个相同的标识符,您可以在单位名称前加上前缀以区分它们。
var
a: tissV01.IXMLMensagemTISS;
b: tissV02.IXMLMensagemTISS;
然而,在您的示例代码中,您需要明确选择要使用的接口。
uses
tissV01, tissV02; //last unit in uses clause gets priority.
type
TMyform = class(TForm)
public
msg3: tissV01.IXMLMensagemTISS; //allowed
msg2: tissV02.IXMLMensagemTISS; //allowed
msgx: IXMLMensagemTISS; //ambigous, will evaluate to tissV02.IXMLMensagemTISS;
end;
uses 子句中的最后一个单元被优先考虑。
这个事实经常被滥用来覆盖内置 类 和与自定义接口的接口。
如果您希望根据某些条件延迟选择,您可以使用条件编译。
或者在uses子句中(利用uses子句顺序的优先效应),
unit DoWork;
interface
uses
{$ifdef V01HasPriority}
tissV02, tissV01;
{$else}
tissV01, tissV02;
{$endif}
或明确在声明中
var
a: {$ifdef useV01} tissV01.IInt {$else} tissV02.IInt {$endif}
然后您可以使用在 {$ifdef ...}
.
之前编译的 {$define V01HasPriority}
在其他地方做出选择
您还可以在 IDE 中声明 {$define ...}
。
Project > Options > Delphi Compiler > Conditional defines
。
如果接口兼容,您只能在运行时选择接口。
这意味着接口继承自一个共同的祖先。
每个接口在 IInterface
中都有一个共同的祖先,但是最好选择一个尽可能接近两者的接口。
然后你声明一个变量作为那个共同的祖先:
var
a: ICommonInterface;
begin
if x=1 then a:= tissV01.NewXMLInterface
else a:= tissV02.NewXMLInterface;
if Supports(a, tissV01.IXMLInt) then tissV01.IXMLInt(a).DoV01Things
else tissV02.IXMLInt(a).DoV02Things;
如果两个接口具有相同的签名,那么事情就容易多了(也更理智)。
var
a: IXMLCommon;
begin
if x=1 then a:= tissV01.NewXMLInterface
else a:= tissV02.NewXMLInterface;
a.DoCommonThings(param1, param2);
集中决策
当然,如果你有很多决定要做,那么(有时)最好将它们集中起来,然后将它们分散到你的程序中。
那么为什么不创建一个可以完成所有决策的单元,就像这样:
unit IvoryTower;
interface
function InterfaceXorY(const person: TPerson): ICommonIntf;
implementation
function InterfaceXorY(const person: TPerson): ICommonIntf;
var
WhatToDo: TSomething;
begin
WhatToDo:= DatabaseY.TableX.GetData(Person);
case WhatToDo of
XYZolog: Result:= Unit1.I1;
Galaga: Result:= Unit2.I2;
Twinbee: Result:= Unit3.I4;
else Assert(false, 'what to do outside of valid range');
end; {case}
end;
我有两个或多个单元需要从第三方下载,当你们的版本有变化时。
我使用 xml 数据绑定来生成单位。它们是这样的:
unit tissV01;
interface
uses .....;
type
IXMLMensagemTISS = interface(IXMLNode)
['{11773827-F0A1-42E0-99E1-E221DFAF8542}']
{ Property Accessors }
end;
function GetmensagemTISS(Doc: IXMLDocument): IXMLMensagemTISS;
implementation
function GetmensagemTISS(Doc: IXMLDocument): IXMLMensagemTISS;
begin
Result := XXXX as IXMLMensagemTISS;
end;
end.
单位tissV02
unit tissV02;
interface
uses .....;
type
{ IXMLMensagemTISS }
IXMLMensagemTISS = interface(IXMLNode)
['{11773827-F0A1-42E0-99E1-E221DFAF8542}']
{ Property Accessors }
property Cabecalho: string read Get_Cabecalho;
end;
function GetmensagemTISS(Doc: IXMLDocument): IXMLMensagemTISS;
implementation
function GetmensagemTISS(Doc: IXMLDocument): IXMLMensagemTISS;
begin
Result := XXXX as IXMLMensagemTISS;
end;
end.
在我的应用程序中,我需要选择我必须使用的单位:
unit test;
interface
uses tissV01,tissV02, .......;
type
TMyform = class(TForm)
public
msg3:IXMLMensagemTISS;
end;
implementation
procedure TMyform.ExecuteMessage:
var
xmlTISS : TXmlDocument;
begin
xmlTISS := TXmlDocument.Create(nil);
if condition then
msg3 := tissV01.GetmensagemTISS(xmlTISS)
else msg3 := tissV02.GetmensagemTISS(xmlTISS);
with msg3.Cabecalho do something;
end;
end.
从逻辑上讲,它不起作用,因为 IXMLMensagemTISS 对两个单元都是通用的。
是否有一些解决方法可以做到这一点而不必更改接口名称 (IXMLMensagemTISS)?
我想简化我的代码,并且我需要在未来维护很多这种类型的单元。问题是所有人都实现了 IXMLMensagemTISS 而我无法改变它。
我不想创建很多 msg 变量,例如 msgV01:=tissv01.GetmensagemTISS、msgV01:=tissv02.GetmensagemTISS、...等等
如果您在不同的单位中有两个相同的标识符,您可以在单位名称前加上前缀以区分它们。
var
a: tissV01.IXMLMensagemTISS;
b: tissV02.IXMLMensagemTISS;
然而,在您的示例代码中,您需要明确选择要使用的接口。
uses
tissV01, tissV02; //last unit in uses clause gets priority.
type
TMyform = class(TForm)
public
msg3: tissV01.IXMLMensagemTISS; //allowed
msg2: tissV02.IXMLMensagemTISS; //allowed
msgx: IXMLMensagemTISS; //ambigous, will evaluate to tissV02.IXMLMensagemTISS;
end;
uses 子句中的最后一个单元被优先考虑。
这个事实经常被滥用来覆盖内置 类 和与自定义接口的接口。
如果您希望根据某些条件延迟选择,您可以使用条件编译。
或者在uses子句中(利用uses子句顺序的优先效应),
unit DoWork;
interface
uses
{$ifdef V01HasPriority}
tissV02, tissV01;
{$else}
tissV01, tissV02;
{$endif}
或明确在声明中
var
a: {$ifdef useV01} tissV01.IInt {$else} tissV02.IInt {$endif}
然后您可以使用在 {$ifdef ...}
.
之前编译的 {$define V01HasPriority}
在其他地方做出选择
您还可以在 IDE 中声明 {$define ...}
。
Project > Options > Delphi Compiler > Conditional defines
。
如果接口兼容,您只能在运行时选择接口。
这意味着接口继承自一个共同的祖先。
每个接口在 IInterface
中都有一个共同的祖先,但是最好选择一个尽可能接近两者的接口。
然后你声明一个变量作为那个共同的祖先:
var
a: ICommonInterface;
begin
if x=1 then a:= tissV01.NewXMLInterface
else a:= tissV02.NewXMLInterface;
if Supports(a, tissV01.IXMLInt) then tissV01.IXMLInt(a).DoV01Things
else tissV02.IXMLInt(a).DoV02Things;
如果两个接口具有相同的签名,那么事情就容易多了(也更理智)。
var
a: IXMLCommon;
begin
if x=1 then a:= tissV01.NewXMLInterface
else a:= tissV02.NewXMLInterface;
a.DoCommonThings(param1, param2);
集中决策
当然,如果你有很多决定要做,那么(有时)最好将它们集中起来,然后将它们分散到你的程序中。
那么为什么不创建一个可以完成所有决策的单元,就像这样:
unit IvoryTower;
interface
function InterfaceXorY(const person: TPerson): ICommonIntf;
implementation
function InterfaceXorY(const person: TPerson): ICommonIntf;
var
WhatToDo: TSomething;
begin
WhatToDo:= DatabaseY.TableX.GetData(Person);
case WhatToDo of
XYZolog: Result:= Unit1.I1;
Galaga: Result:= Unit2.I2;
Twinbee: Result:= Unit3.I4;
else Assert(false, 'what to do outside of valid range');
end; {case}
end;