如何区分TStringList中的Pointer和TObject条目?

时间:2013-11-15 21:04:39

标签: delphi

我们可以将字符串连同一些关联对象一起添加到TStringList:

list: TStringList;
obj: MyObject;

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

此外,简单地将字符串与指针连接起来非常方便,即整数值,如下所示:

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

如果我以后访问此列表中的对象,如何知道它是MyObject还是只是指针?我想要这样的东西:

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

但如果 list.Objects [i] 只是一个指针,这显然会导致访问冲突。 提前谢谢!

4 个答案:

答案 0 :(得分:8)

如果要将整数和对象安全地存储到一个字符串列表中,请定义一个变量容器类来保存整数或对象。

以下是大致概述的类,包括测试项目。

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.

答案 1 :(得分:5)

  

完全有可能添加一个恰好指向的整数   到一个对象。同样,指向一个指针是完全可能的   列表中已经释放了对象的对象。

最重要的是,您可以开始在内存中查找所有想要的内容,没有防弹方式来了解您的stringlist是否包含整数或指针。

因为你不应该混合不同的类型,所以也没有需要知道。更好的方法是创建两个包含 Stringlist的类,并使外部类类型安全可用。那么你的问题就成了一个问题。

示例 假设您的Delphi版本不支持泛型

  TStringIntegerMap = class
    private FStringIntegerList: TStringList;
  public
    procedure Add(const Key: string; Value: Integer);
    ... // Add the other required equivalent TStringlist methods 
  end;

  TStringObjectMap = class
    private FStringObjectList: TStringList;
  public
    procedure Add(const Key: string; Value: TObject);
    ... // Add the other required equivalent TStringlist methods 
  end;

请注意,这只是为了向您提供有关如何实现此类的要点。

答案 2 :(得分:3)

TObject实际上是一个指针。因此,考虑到后者是前者,根本无法区分指针和TObject。

如果您对某个对象有所了解,并且需要稍后检索该知识,请不要丢弃该知识。如果您以后需要知道某些事情,请记住它。

答案 3 :(得分:0)

正如@DavidHeffernan正确指出的那样,类类型是指针,因此它们在语义上是等价的,没有存储类型指示就无法区分它们。

但是,如果你要问 “如何找出给定的任意指针指向对象实例?” ,有一个解决方案:

/// <summary>
///   Verifies that the argument points to valid object instance.
/// </summary>
/// <exception cref="EAccessViolation">
///   If segmentation fault occurs while reading VMT and/or its field from the
///   specified memory address.
/// </exception>
/// <remarks>
///   Delphi only, incompatible with FPC.
/// </remarks>
/// <example>
///   <code>
/// procedure TForm1.FormCreate(Sender: TObject);
/// begin
///   ShowMessage(BoolToStr(IsInstance(Self), True));
/// end;
///  </code>
/// </example>
function IsInstance(Data: Pointer): Boolean;
var
  VMT: Pointer;
begin
  VMT := PPointer(Data)^;
  Result := PPointer(PByte(VMT) + vmtSelfPtr)^ = VMT;
end;

我发布了整个内联文档,所以我觉得更多的注释是不必要的,但我想回顾一下你的例子中Pointer(7)之类的故意无效指针肯定会导致访问冲突错误。因此,如果指针的较高Word为零,则可以执行初步检查(与Windows.IS_INTRESOURCE宏中的逻辑相同:

function Is_IntResource(lpszType: PChar): BOOL;
begin
  Result := ULONG_PTR(lpszType) shr 16 = 0;
end;