【问题标题】:Duck typing in Delphi 2007?鸭子在 Delphi 2007 中打字?
【发布时间】:2012-02-29 10:52:17
【问题描述】:

问题:

有没有办法使用 Delphi 2007 进行鸭式打字(即没有泛型和高级 Rtti 功能)?


用于 Delphi 2010 及以后的鸭子打字资源:

上次编辑:

我对上面列出的资源进行了深入研究,并在此处研究了每个发布的答案。

我最终完善了我的要求,并为此问题提出了follow up post

【问题讨论】:

    标签: delphi delphi-2007 duck-typing


    【解决方案1】:

    在 ObjAuto.pas 和可调用变体类型的帮助下,它应该是可能的(用 XE 编写,但也应该在 Delphi 7 或更低版本中运行):

    unit DuckTyping;
    
    interface
    
    function Duck(Instance: TObject): Variant;
    
    implementation
    
    uses
      ObjAuto,
      SysUtils,
      TypInfo,
      Variants;
    
    type
      TDuckVarData = packed record
        VType: TVarType;
        Reserved1, Reserved2, Reserved3: Word;
        VDuck: TObject;
        Reserved4: LongWord;
      end;
    
      TDuckVariantType = class(TPublishableVariantType)
      protected
        function GetInstance(const V: TVarData): TObject; override;
      public
        procedure Clear(var V: TVarData); override;
        procedure Copy(var Dest: TVarData; const Source: TVarData;
          const Indirect: Boolean); override;
        function DoFunction(var Dest: TVarData; const V: TVarData;
          const Name: string; const Arguments: TVarDataArray): Boolean; override;
      end;
    
    var
      DuckVariantType: TDuckVariantType;
    
    { TDuckVariantType }
    
    procedure TDuckVariantType.Clear(var V: TVarData);
    begin
      V.VType := varEmpty;
      TDuckVarData(V).VDuck := nil;
    end;
    
    procedure TDuckVariantType.Copy(var Dest: TVarData; const Source: TVarData;
      const Indirect: Boolean);
    begin
      if Indirect and VarDataIsByRef(Source) then
        VarDataCopyNoInd(Dest, Source)
      else
      begin
        with TDuckVarData(Dest) do
        begin
          VType := VarType;
          VDuck := TDuckVarData(Source).VDuck;
        end;
      end;
    end;
    
    function TDuckVariantType.DoFunction(var Dest: TVarData; const V: TVarData;
      const Name: string; const Arguments: TVarDataArray): Boolean;
    var
      instance: TObject;
      methodInfo: PMethodInfoHeader;
      paramIndexes: array of Integer;
      params: array of Variant;
      i: Integer;
      ReturnValue: Variant;
    begin
      instance := GetInstance(V);
      methodInfo := GetMethodInfo(instance, ShortString(Name));
      Result := Assigned(methodInfo);
      if Result then
      begin
        SetLength(paramIndexes, Length(Arguments));
        SetLength(params, Length(Arguments));
        for i := Low(Arguments) to High(Arguments) do
        begin
          paramIndexes[i] := i + 1;
          params[i] := Variant(Arguments[i]);
        end;
    
        ReturnValue := ObjectInvoke(instance, methodInfo, paramIndexes, params);
        if not VarIsEmpty(ReturnValue) then
          VarCopy(Variant(Dest), ReturnValue);
      end
      else
      begin
        VarClear(Variant(Dest));
      end;
    end;
    
    function TDuckVariantType.GetInstance(const V: TVarData): TObject;
    begin
      Result := TDuckVarData(V).VDuck;
    end;
    
    function Duck(Instance: TObject): Variant;
    begin
      TDuckVarData(Result).VType := DuckVariantType.VarType;
      TDuckVarData(Result).VDuck := Instance;
    end;
    
    initialization
      DuckVariantType := TDuckVariantType.Create;
    
    finalization
      FreeAndNil(DuckVariantType);
    
    end.
    

    您可以像这样简单地使用它:

    type
      {$METHODINFO ON}
      TDuck = class
      public // works in XE, not sure if it needs to be published in older versions
        procedure Quack;
      end;
    
    procedure TDuck.Quack;
    begin
      ShowMessage('Quack');
    end;
    
    procedure DoSomething(D: Variant);
    begin
      D.Quack;
    end;
    
    var
      d: TDuck;
    begin
      d := TDuck.Create;
      try
        DoSomething(Duck(d));
      finally
        d.Free;
      end;
    end;
    

    【讨论】:

    • +1:这是我所期望的。我用 Delphi 7 和 Delphi 2007 对其进行了测试:它有效。这篇文章是一个可以接受的答案。丹克谢尔!
    • +1 Allen Bauer 以前曾谈到过这种事情,例如blogs.embarcadero.com/abauer/2007/06/13/36013 但我记得他们更多。或者可能是在 Nick 的旧 Codegear 博客上。
    • +1,我喜欢这个。 @menjaraz,您应该考虑更改接受的答案。
    • @Cosmin Prund:你的同意没关系。
    • @Stefan Glienke:你在生产中使用过它吗?
    【解决方案2】:

    快速回答:

    没有意义

    更长的答案:根据wiki page“Duck Typing”的标识:

    在鸭子类型中,人们只关心对象的那些被使用的方面,而不是对象本身的类型。例如,在非鸭子类型语言中,可以创建一个函数,该函数接受一个鸭子类型的对象并调用该对象的 walk 和 quack 方法。在鸭子类型语言中,等效函数将获取任何类型的对象并调用该对象的 walk 和 quack 方法。如果对象没有被调用的方法,则该函数会发出运行时错误信号。

    等效的 Delphi 不可编译代码如下所示:

    procedure DoSomething(D);
    begin
      D.Quack;
    end;
    

    我故意没有为D 指定类型,因为那样会破坏目的。 Delphi 是静态类型的,所以这永远不会工作。如果您需要此功能来实现一些小功能,您可以使用 InterfacesRTTI 并获得如下内容:

    procedure DoSomething(D:TObject);
    begin
      (D as ISomeIntf).Quack;
    end;
    

    如果你能获得 RTTI:

    procedure DoSomething(D:TObject);
    begin
      CallQuackUsingRTTI(D);
    end;
    

    我个人使用RTTI 方法来识别(和操作)列表对象,使代码同时适用于TList 后代和通用TList<T> 变体。

    从中得出的结论应该是:即使在最新版本的 Delphi 中具有高级功能(泛型和综合 RTTI),您也只能在有限的功能和大量努力下接近 Duck 打字。这根本不在 Delphi 的 DNA 中(因为 Delphi 的 DNA 说“静态类型”),但您可能能够获得足够接近的东西,并且付出很多努力,并且仅用于特定功能.也许如果您告诉我们您想要什么具体功能,我们就能想出办法。

    【讨论】:

    • 感谢您的回答。我深入研究了我引用的 3 个资源的源代码并得出了这个结论:它们使用接口、泛型和成熟的 Rtti 实现了 Duck 类型(Duck Duck Delphi 以 Delphi XE2 为目标,其余的可以使用 Delphi XE)。
    • 我希望能够在 Delphi 2007 中以 Duck 输入方式操作任何实现 1) function Count: Integer; 2) function GetItem(const index: Integer): TObject; 3) procedure Add(const AObject: TObject); 4) procedure Clear; 的任意列表对象。
    • @menjaraz:然后创建一个包含这4个方法的接口,并使用Supports()检查列表对象是否实现了这个接口。
    • @mghie:这排除了对实现了我列举的方法但没有接口的其他对象的操作(以 Duck 类型的方式)。我说的对吗?
    • @menjaraz:是的,但这会是个问题吗?
    【解决方案3】:

    这是一个需要您创建类型库的想法。

    使用 OLE 自动化类型,并实现调度接口(双 COM 对象)。

    现在你现在你可以在该类型之后编写任何你想要的东西,我们将在运行时找出它是否有效或崩溃。欢迎使用动态类型。

    procedure DoSomething(D:OleVariant);
    begin
      D.Quack; // Might work, might blow up.
    end;
    

    我认为它丑陋,但其他人可能不会。

    【讨论】:

    • +1。这似乎是一个非常非常长的路要走。我尝试在控制台应用程序中将其实现为测试但失败了(当我意识到我需要一个已注册的类型库时放弃了) - 但我的 COM 和 OLE 知识几乎接近于零。另一方面,您的函数没有返回类型:)
    • Delphi 使得编写双重类型变得非常容易,并且在应用程序内部,您需要一个类型库,但是您可以在本地注册它,而无需在 Delphi 外部注册,使用 SideBySide (sxs) COM .通过丑陋,是的,我的意思是“很长的路要走”,但它确实有效。但是,如果有人想在与我有任何关系的应用程序中这样做,我会立即拒绝。但由于它满足了 OP 的需求,至少严格来说,它是一种选择。
    • +1,但我会删除第一句话。这并没有什么丑陋的(一旦决定使用鸭式打字,这是 Delphi 中的一个陌生概念)。我宁愿看到这是公认的答案......
    猜你喜欢
    • 2012-10-26
    • 2011-11-25
    • 2011-03-23
    • 1970-01-01
    • 2011-05-11
    • 2020-02-13
    • 1970-01-01
    • 2015-08-01
    • 1970-01-01
    相关资源
    最近更新 更多