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

曲折

我有两种重载方法:

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

他们做的是几乎相同的重复代码,除了从次要取决于是否变化Value是变体或TObject

我想使用一种常见的方法:

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

因此,我既可以通过也可以通过,也Variant可以TObject从这SetProp两种类型中区分出来。我有什么选择?


编辑:现在我使用:

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我宁愿从常见的类型“容器”中检测类型。

我可以使用TVarRec

VarRec: TVarRec;

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

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


编辑2: 我想做什么?我正在尝试扩展TObjectSetProp 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尚未实施。

戴夫·奥尔森

您正在与编译器战斗;您应该继续使用重载。

“我宁愿从普通类型的“容器”中检测类型。”

您的选择是变体或无类型的指针。您将必须解压缩“ Value”参数。使用无类型的指针,您将必须完成所有工作。使用变体,您将必须完成大部分工作。很乱。

“它们执行几乎相同的重复代码,只是根据Value是Variant还是TObject而有一些细微的变化。”

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

另外,使用重载方法,我不会有一个接受variant的方法(尽管下面的示例可以)。对于字符串,整数,双精度型等都有单独的重载。

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;

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章