如何将Variant或TObject传递给相同的方法参数?

时间:2017-06-10 06:26:08

标签: delphi delphi-7

我有两种重载方法:

procedure TProps.SetProp(Value: TObject); overload;
procedure TProps.SetProp(const Value: Variant); overload;

除了次要变体之外,它们的重复代码几乎完全相同,具体取决于Value是变体还是TObject

我想使用一种常用方法:

procedure TProps.DoSetProp(Value: <what type here?>); // <--

因此,我可以从Variant传递TObjectSetProp,并能够区分这两种类型。我有什么选择?

编辑:我现在用过:

procedure TProps.DoSetProp(Value: Pointer; IsValueObject: Boolean);
begin
  // common code...
  if IsValueObject then
    PropValue.Obj := Value
  else
    PropValue.V := PVariant(Value)^;
  // common code...
  if IsValueObject then
    PropValue.Obj := Value
  else
    PropValue.V := PVariant(Value)^;
  // etc...
end;

和重载方法:

procedure TProps.SetProp(const Value: Variant); overload;
begin
  DoSetProp(@Value, False);
end;

procedure TProps.SetProp(Value: TObject); overload;
begin
  DoSetProp(Value, True);  
end;

由于IsValueObject,我不确定我喜欢这个解决方案。我宁愿从普通类型&#34;容器&#34;。

中检测出类型

我可以使用TVarRec

VarRec: TVarRec;

// for Variant:
VarRec.VType := vtVariant;
VarRec.VVariant := @Value;
// for TObject
VarRec.VType := vtObject;
VarRec.VObject := Value;

并将VarRec传递给常用方法。但我也不确定是否喜欢它。

编辑2: 我想做什么? 我试图扩展任何TObject的属性与SetProp API类似。

以下是整个MCVE:

function ComparePointers(A, B: Pointer): Integer;
begin
  if Cardinal(A) = Cardinal(B) then
    Result := 0
  else if Cardinal(A) < Cardinal(B) then
    Result := -1
  else
    Result := 1
end;

type
  TPropValue = class
  private
    V: Variant;
    Obj: TObject;
    procedure SetValue(const Value: Pointer; IsValueObject: Boolean);
  end;

  TPropNameValueList = class(TStringList)
  public
    destructor Destroy; override;
    procedure Delete(Index: Integer); override;
  end;

  TObjectProps = class
  private
    BaseObject: TObject;
    PropList: TPropNameValueList;
  public
    constructor Create(AObject: TObject);
    destructor Destroy; override;
  end;

  TProps = class(TComponent)
  private
    FList: TObjectList;
  protected
    procedure DoSetProp(AObject: TObject; const PropName: string; const Value: Pointer; IsValueObject: Boolean);
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  public
    function Find(AObject: TObject; var Index: Integer): Boolean;
    procedure SetProp(AObject: TObject; const PropName: string; const Value: Variant); overload;
    procedure SetProp(AObject: TObject; const PropName: string; Value: TObject); overload;
    function RemoveProp(AObject: TObject; const PropName: string): Boolean;
    function RemoveProps(AObject: TObject): Boolean;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

{ TPropValue }
procedure TPropValue.SetValue(const Value: Pointer; IsValueObject: Boolean);
begin
  if IsValueObject then
    Obj := Value
  else
    V := PVariant(Value)^;
end;

{ TPropNameValueList }
destructor TPropNameValueList.Destroy;
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
    Objects[I].Free; // TPropValue
  inherited;
end;

procedure TPropNameValueList.Delete(Index: Integer);
begin
  Objects[Index].Free;
  inherited;
end;

{ TObjectProps }
constructor TObjectProps.Create(AObject: TObject);
begin
  BaseObject := AObject;
  PropList := TPropNameValueList.Create;
  PropList.Sorted := True;
  PropList.Duplicates := dupError;
end;

destructor TObjectProps.Destroy;
begin
  PropList.Free;
  inherited;
end;

{ TProps }
constructor TProps.Create(AOwner: TComponent);
begin
  inherited;
  FList := TObjectList.Create(true);
end;

procedure TProps.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited;
  if (Operation = opRemove) and (AComponent <> nil) then
  begin
    RemoveProps(AComponent);
  end;
end;

destructor TProps.Destroy;
begin
  FList.Free;
  inherited;
end;

function TProps.Find(AObject: TObject; var Index: Integer): Boolean;
var
  L, H, I, C: Integer;
begin
  Result := False;
  L := 0;
  H := FList.Count - 1;
  while L <= H do
  begin
    I := (L + H) shr 1;
    C := ComparePointers(TObjectProps(FList[I]).BaseObject, AObject);
    if C < 0 then L := I + 1 else
    begin
      H := I - 1;
      if C = 0 then
      begin
        Result := True;
        L := I;
      end;
    end;
  end;
  Index := L;
end;

procedure TProps.DoSetProp(AObject: TObject; const PropName: string; const Value: Pointer; 
  IsValueObject: Boolean);
var
  OP: TObjectProps;
  PropValue: TPropValue;
  Index, NameIndex: Integer;
  Found: Boolean;
  I: Integer;
begin
  Found := Find(AObject, Index);
  if not Found then
  begin
    OP := TObjectProps.Create(AObject);
    if AObject is TComponent then
      TComponent(AObject).FreeNotification(Self);
    PropValue := TPropValue.Create;
    PropValue.SetValue(Value, IsValueObject);    
    OP.PropList.AddObject(PropName, PropValue);
    FList.Insert(Index, OP);
  end
  else
  begin
    OP := TObjectProps(FList[Index]);
    NameIndex := OP.PropList.IndexOf(PropName);
    if NameIndex <> -1 then
    begin
      PropValue := TPropValue(OP.PropList.Objects[NameIndex]);
      PropValue.SetValue(Value, IsValueObject);      
    end
    else
    begin
      PropValue := TPropValue.Create;
      PropValue.SetValue(Value, IsValueObject);      
      OP.PropList.AddObject(PropName, PropValue);
    end;
  end;
end;

procedure TProps.SetProp(AObject: TObject; const PropName: string; const Value: Variant);
begin
  DoSetProp(AObject, PropName, @Value, False);
end;

procedure TProps.SetProp(AObject: TObject; const PropName: string; Value: TObject);
begin
  DoSetProp(AObject, PropName, Value, True);
end;

function TProps.RemoveProp(AObject: TObject; const PropName: string): Boolean;
var
  Index, NameIndex: Integer;
  OP: TObjectProps;
begin
  Result := False;
  if not Find(AObject, Index) then Exit;
  OP := TObjectProps(FList[Index]);
  NameIndex := OP.PropList.IndexOf(PropName);
  if NameIndex <> -1 then
  begin
    OP.PropList.Delete(NameIndex);
    Result := True;
  end;
end;

function TProps.RemoveProps(AObject: TObject): Boolean;
var
  Index: Integer;
  OP: TObjectProps;
begin
  if not Find(AObject, Index) then
  begin
    Result := False;
    Exit;
  end;
  OP := TObjectProps(FList[Index]);
  Result := FList.Remove(OP) <> -1;
end;

用法:

Props := TProps.Create(Self);
Props.SetProp(Button1, 'myprop1', Self); // TObject property
Props.SetProp(Button1, 'myprop2', 666); // variant
Props.SetProp(Button2, 'myprop', 'Hello'); // variant
Props.SetProp(MyObject, 'foo', 123.123);

注意:TProps.GetProp尚未实施。

1 个答案:

答案 0 :(得分:2)

你正在与编译器作斗争;你应该继续使用重载。

&#34;我宁愿从普通类型的容器中检测出类型&#39;。&#34;

您的选择是变体或无类型指针。你将不得不打开&#34;价值&#34;参数。使用无类型指针,您将不得不完成所有工作;使用变体,您将不得不完成大部分工作。非常凌乱。

&#34;它们几乎完全相同的重复代码,除了根据Value是Variant还是TObject的微小变化。&#34;

如果确实如此,那么你仍然应该继续使用重载但是添加一个内部的&#34; SetProp&#34;采取&#34;标准化&#34;的方法做实际工作的数据。你的重复&#34; code是属性值的设置。但你仍然有特定的代码来编写来破解传入的&#34;值&#34;参数是否有一个方法接受&#34;容器&#34;采用您想要接受的各种类型的类型或多个重载方法。在one-method-container类型中,您将拥有一个(复杂的)if-then-else块,它会破坏Value。在重载方法类型中没有if-testing;你只需破解每个方法接受的类型的值。 主要优点是您的对象更好地记录:您可以看到哪些类型可以接受&#34;值&#34;而且,更好的是,编译器会帮助你,因为它知道&#34;什么类型是可以接受的使用单方法方法,编译器将无法帮助您强制执行&#34; Value&#34 ;;你正在做所有的工作。

另外,使用重载方法,我不会有一个接受变体的方法(尽管下面的例子也是如此)。为string,integer,double等每个都有一个单独的重载。

type
   TNormalizedPropValue = record
   // ....
   end;


procedure TProps.internalSetProp(Value : TNormalizedPropValue);

begin
//
// Set the property value from the "Normalized" pieces and parts.
//
end;

procedure TProps.SetProp(Value : TObject);

var  
   NormalizedObjectPropValue : TNormalizedPropValue;

begin
   // Copy the pieces and parts from "Value" into NormalizedObjectPropValue
   //

   internalSetProp(NormalizedObjectPropValue);
end;

procedure TProps.SetProp(Value : variant);

var  
   NormalizedVariantPropValue : TNormalizedPropValue;

begin
   // Crack "Value" variant and copy the pieces and parts into NormalizedVariantPropValue
   //

   internalSetProp(NormalizedVariantPropValue);
end;