Duck typing in Delphi 2007?

前端 未结 3 1845
你的背包
你的背包 2020-12-05 16:02

Question:

Is there a way to do duck typing with Delphi 2007 (i.e. without generics and advanced Rtti features)?


Duck typing Re

3条回答
  •  猫巷女王i
    2020-12-05 16:46

    With the help of the ObjAuto.pas and invokable variant types it should be possible (written in XE but should also run in Delphi 7 or lower):

    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.
    

    You can simply use it like this:

    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;
    

提交回复
热议问题