Delphi接口助手/解决方法

16

我意识到 Delphi 不支持接口帮助器,但是在阅读了几个 SO 主题和 Spring4D 的源代码等资料后,我想知道是否有方法可以实现以下目标?源代码注释基本上概括了我试图做的事情,因此在此呈现:

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.
任何关于如何使代码在 **** 显示的想法或建议?我知道答案可能是完全“不行”,但似乎有一些非常聪明的变通方法正在被实现,也许有人比我聪明知道怎么做?(Delphi 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;

1
也许我没有完全理解这个问题,但是简单的(接口)继承有什么问题吗?将IMyThingHelper作为IMyThing的后代,并拥有一个实现额外功能的TMyThingHelper - Rudy Velthuis
1
@Rudy 接口助手的目的是并非每个实现该接口的类都必须实现该方法。当然,它们只能在它们“帮助”的接口的 API 上操作。您应该熟悉扩展方法的概念。 - Stefan Glienke
3
如果有接口助手会很不错。这个解决方案只是通过包装接口来模拟这样的助手。这是一种相当笨拙的做法,但我会将包装器实现为一个类,并且有一个继承自 IMyThing 的助手 接口。实现仍然可以是一个包装器,但在许多需要原接口的情况下,可以使用这样的派生接口。 - Rudy Velthuis
与其试图强行让语言做它不想做的事情,我认为Rudy在上面的评论中提出的建议似乎是最好的妥协。 - David Heffernan
@Stefan 使用帮助程序的问题在于它无法用于接口。 - David Heffernan
显示剩余3条评论
2个回答

4

为什么不能使用其他接口?

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.

13
为什么不行?可能是因为提问者无法修改实现对象。这就是为什么要使用帮助器/扩展方法的原因。 - David Heffernan
在这种情况下,我使用包装器(我相信有更好的术语或模式名称,但我不知道)。虽然不完全相同,但在许多情况下足够使用。 - Rudy Velthuis
我已经使用了接口继承,但这并不是我目前想要实现的。 - Rick Wheeler
@Rick Remy并不建议接口继承。我也看不出这会对你有什么帮助。Remy在这里提出的建议也不是辅助程序的替代品。 - David Heffernan

3

有两种实现方法:

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

  • 另一种方法是使用absolute


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

我之前曾经写过这个问题的博客。不幸的是,我的“辅助记录”Enumerable<T>中有很多通用方法,导致编译器变得非常缓慢。


4
“absolute”是一个可憎的词,如果有一天被从语言中移除,我并不会感到奇怪。 - Rudy Velthuis
谢谢Stefan,我已经读了你的博客几次,它给了我一个良好的开端,但不幸的是Delphi似乎“无法做到”。我对“absolute”关键字没有经验,所以我需要进一步调查。 - Rick Wheeler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接