Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to distinguish between Pointer and TObject entries in a TStringList?

Tags:

delphi

We can add strings along with some associated objects to a TStringList:

list: TStringList;
obj: MyObject;

obj := MyObject.Create();
list.AddObject("real object", obj);

In addition it can be very handy to simply connect a string with a Pointer, i.e., an integer value, like this:

list.AddObject("just an index", Pointer(7));

If I later access to an object in this list how to know if it is a MyObject or simply a Pointer? I want something like this:

for i := 0 to list.Count-1 do
  if list.Objects[i] is MyObject then
  begin
    // ...
    // Do something with list.Objects[i]
    // ...
  end;

but this obviously leads to access violation if list.Objects[i] is just a Pointer. Thanks in advance!

like image 658
grafd Avatar asked Nov 15 '13 21:11

grafd


1 Answers

If you want to safely store both integers and objects into one stringlist, define a variant container class to hold integers or objects.

Below is such a class roughly outlined including a test project.

unit VariantContainer;

interface

uses Variants,SysUtils;

Type
  TVariantContainer = class
    private
      FVariant : Variant;
    public
      constructor Create(aValue: Integer); overload;
      constructor Create(aValue: TObject); overload;
      function IsInteger: Boolean;
      function IsObject: Boolean;
      function AsObject: TObject;
      function AsInteger: Integer;
  end;

implementation

function TVariantContainer.AsInteger: Integer;
begin
  if not IsInteger then
    raise Exception.Create('Variant is not Integer');
  Result := FVariant;    
end;

function TVariantContainer.AsObject: TObject;
begin
  if not IsObject then
    raise Exception.Create('Variant is not TObject');
  Result := TVarData(FVariant).VPointer;
end;

function TVariantContainer.IsInteger: Boolean;
begin
  Result := VarIsType( FVariant, varInteger);
end;

function TVariantContainer.IsObject: Boolean;
begin
  Result := VarIsType(FVariant, varByRef);
end;

constructor TVariantContainer.Create(aValue: Integer);
begin
  Inherited Create;
  FVariant := aValue;
end;

constructor TVariantContainer.Create(aValue: TObject);
begin
  Inherited Create;
  TVarData(FVariant).VType:= VarByRef;
  TVarData(FVariant).VPointer:= aValue;
end;

end.

program ProjectTestVariantContainer;

{$APPTYPE CONSOLE}
uses
  Variants,SysUtils,Classes,VariantContainer;

Type
  TMyObj = class
    s:String;
  end;

var
  sList: TStringList;
  o: TMyObj;
  i: Integer;
begin
  o := TMyObj.Create;
  o.s := 'Hello';
  sList := TStringList.Create;
  sList.OwnsObjects := True;  // List owns container objects
  sList.AddObject('AnInteger',TVariantContainer.Create(3));
  sList.AddObject('AnObject',TVariantContainer.Create(o));
  for i := 0 to sList.Count-1 do
  begin
    if Assigned(sList.Objects[i]) then 
    begin
      if TVariantContainer(sList.Objects[i]).IsInteger then
        WriteLn( TVariantContainer(sList.Objects[i]).AsInteger)
      else
      if TVariantContainer(sList.Objects[i]).IsObject then
        WriteLn( TMyObj(TVariantContainer(sList.Objects[i]).AsObject).s);
    end;
  end;
  ReadLn;
  o.Free;
  sList.Free;  
end.
like image 140
LU RD Avatar answered Nov 15 '22 06:11

LU RD