【问题标题】:Delphi interface helpers / workaroundsDelphi 界面助手/解决方法
【发布时间】:2014-09-07 17:49:26
【问题描述】:

我意识到Delphi不支持接口助手,但是在阅读了几个SO主题和Spring4D等资源之后,我想知道有没有什么方法可以实现以下?源代码 cmets 几乎总结了我正在尝试做的事情,所以这里是:

program IHelper;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Spring,
  System.SysUtils;

type

  IMyThing = interface
  ['{01E799A5-9141-4C5E-AA85-B7C9792024D9}']
    procedure BasicThing;
  end;

  TMyThing = class(TInterfacedObject, IMyThing)
  strict private
    procedure BasicThing;
  end;

  IMyThingHelper = record
  private
    FOutage: IMyThing;
  public
    class operator Implicit(const Value: IMyThing): IMyThingHelper;
    procedure HelpfulThing;
  end;

  TMyThingHelper = class helper for TMyThing
  public
    class procedure ObjectThing;
  end;

{ TOutage }

procedure TMyThing.BasicThing;
begin
  Writeln('Basic Thing');
end;


{ IOutageHelper }

procedure IMyThingHelper.HelpfulThing;
begin
  Writeln('Helpful thing');
end;

class operator IMyThingHelper.Implicit(const Value: IMyThing): IMyThingHelper;
begin
  Result.FOutage := Value;
end;

{ TMyThingHelper }

class procedure TMyThingHelper.ObjectThing;
begin
  Writeln('Object thing');
end;

var
  LThing: IMyThing;

begin
  try
    LThing := TMyThing.Create;
    LThing.BasicThing;
    //LThing.HelpfulThing;               // <--- **** prefer this syntax but obviously does not compile
    IMyThingHelper(LThing).HelpfulThing; // <--- this works ok but prefer not to have to cast it here

    //LThing.ObjectThing;                // <--- obviously does not compile
    (LThing as TMyThing).ObjectThing;    // <--- class helpers work ok but no good for interfaces

    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

关于如何使此代码在 **** 显示的地方工作的任何想法或建议? 我知道答案可能是彻底的“不”,但似乎有一些非常聪明的解决方法正在完成,也许比我聪明得多的人知道怎么做? (德尔福 XE5)

另一个例子

var
   dataObject: IDataObject;

//Get clipboard IDataObject
OleGetClipboard({out}dataObject);

//Check if they want us to move or copy what's on the clipboard
preferredDropEffect: DWORD := dataObject.GetPreferredDropEffect;

//...do the stuff with the clipboard

//Tell them what we did
dataObject.SetPerformedDropEffect(DROPEFFECT_NONE); //we moved the underlying data; sender need not do anything
dataObject.SetPasteSucceeded(DROPEFFECT_MOVE); //Paste complete

有一个助手:

TDataObjectHelper = interface helper for IDataObject
public
   function GetPreferredDropEffect(DefaultPreferredDropEffect: DWORD=DROPEFFECT_NONE): DWORD;
end;

function TDataObjectHelper.GetPreferredDropEffect(DefaultPreferredDropEffect: DWORD=DROPEFFECT_NONE): DWORD;
begin
{
    DROPEFFECT_NONE   = 0;  //Drop target cannot accept the data.
    DROPEFFECT_COPY   = 1;  //Drop results in a copy. The original data is untouched by the drag source.
    DROPEFFECT_MOVE   = 2;  //Drag source should remove the data.
    DROPEFFECT_LINK   = 4;  //Drag source should create a link to the original data.
    DROPEFFECT_SCROLL = 0x80000000 //Scrolling is about to start or is currently occurring in the target. This value is used in addition to the other values.
}
    if TDataObjectHelper.ContainsFormat(Source, CF_PreferredDropEffect) then
        Result := TDataObjectHelper.GetUInt32(Source, CF_PREFERREDDROPEFFECT)
    else
        Result := DefaultDropEffect;
end;

【问题讨论】:

  • 也许我不太明白这个问题,但是简单(接口)继承有什么问题?使 IMyThingHelper 成为 IMyThing 的后代,并拥有一个 TMyThingHelper ,实现额外的功能。
  • @Rudy 拥有接口助手的目的是,并非每个实现此类接口的类都必须实现该方法。当然,他们只能对他们正在“帮助”的接口的 API 进行操作。您应该熟悉扩展方法的概念。
  • 是的,如果有界面助手那就太好了。该解决方案仅通过包装接口来模拟此类助手。这是一种相当笨拙的做事方式,但我会将包装器实现为类并有一个从IMyThing 继承的帮助器interface。实现仍然可以是一个包装器。但是在许多需要原始接口的情况下,您可以使用这样的派生接口。
  • Rudy 在上面的评论中提出的建议对我来说似乎是最好的妥协,而不是试图硬着头皮去做它不想做的事情。
  • @RudyVelthuis 您能提供样品或您提出的解决方案吗?

标签: delphi interface delphi-xe5 spring4d


【解决方案1】:

有两种方法可以实现:

  • 有一个变量IMyThingHelper 并将您的接口分配给它,然后在记录变量上调用“扩展方法”。

  • 另一个是使用absolute:


var
  LThing: IMyThing;
  LHelper: IMyThingHelper absolute LThing;
begin
  LThing := TMyThing.Create;
  LHelper.HelpfulThing;

不久前我在博客上写过这个issue。不幸的是,在我的情况下,“帮助记录”Enumerable&lt;T&gt; 有太多通用方法,以至于编译器的速度大大降低。

【讨论】:

  • absolute 是一种可憎的东西,如果有一天它从语言中删除,我不会怀疑。
  • 感谢 Stefan,我已经阅读了您的博客几次,它给了我一个良好的开端,但不幸的是,Delphi 似乎“做不到”。我没有使用“绝对”关键字的经验,因此我需要进一步调查。
【解决方案2】:

为什么不直接使用另一个界面?

program IHelper;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Spring,
  System.SysUtils;

type

  IMyThing = interface
  ['{01E799A5-9141-4C5E-AA85-B7C9792024D9}']
    procedure BasicThing;
  end;

  IMyThingHelper = interface
  ['{...}']
    procedure HelpfulThing;
  end;

  TMyThing = class(TInterfacedObject, IMyThing, IMyThingHelper)
  strict private
    procedure BasicThing;
    procedure HelpfulThing;
  end;

{ TOutage }

procedure TMyThing.BasicThing;
begin
  Writeln('Basic Thing');
end;

{ IOutageHelper }

procedure TMyThing.HelpfulThing;
begin
  Writeln('Helpful thing');
end;

var
  LThing: IMyThing;
  LHelper: IMyThingHelper;
begin
  try
    LThing := TMyThing.Create;
    LThing.BasicThing;
    if Supports(LThing, IMyThingHelper, LHelper) then
      LHelper.HelpfulThing;
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

【讨论】:

  • 为什么不呢?大概是因为询问者无法修改实现对象。这就是为什么要使用助手/扩展方法。
  • 在这种情况下,我使用包装器(我确信有一个更好的术语或模式名称,我只是不知道)。不完全一样,但在很多情况下就足够了。
  • 我已经使用接口继承,但这不是我目前想要实现的目标。
  • @Rick Remy 不建议接口继承。并不是说我也能看到这对你有帮助。雷米在这里建议的也不能替代助手。
猜你喜欢
  • 2011-12-15
  • 2010-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-19
  • 2021-01-04
  • 1970-01-01
相关资源
最近更新 更多