Delphi 如何在TStringList中区分指针和TObject条目?

Delphi 如何在TStringList中区分指针和TObject条目?,delphi,Delphi,我们可以将字符串以及一些相关对象添加到TStringList: list: TStringList; obj: MyObject; obj := MyObject.Create(); list.AddObject("real object", obj); 此外,将字符串与指针(即整数值)连接起来非常方便,如下所示: list.AddObject("just an index", Pointer(7)); for i := 0 to list.Count-1 do if list.Obj

我们可以将字符串以及一些相关对象添加到TStringList:

list: TStringList;
obj: MyObject;

obj := MyObject.Create();
list.AddObject("real object", obj);
此外,将字符串与指针(即整数值)连接起来非常方便,如下所示:

list.AddObject("just an index", Pointer(7));
for i := 0 to list.Count-1 do
  if list.Objects[i] is MyObject then
  begin
    // ...
    // Do something with list.Objects[i]
    // ...
  end;
如果我以后访问此列表中的对象,如何知道它是MyObject还是指针?我想要这样的东西:

list.AddObject("just an index", Pointer(7));
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]只是一个指针,这显然会导致访问冲突。 提前谢谢

加上一个正好在点上的整数是完全可能的 指向一个对象。同样,完全有可能有一个指向 对象,其中该对象已被释放

总之,您可以开始在内存中随意查看,没有防弹的方法来知道stringlist是否包含整数或指针

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

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


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

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


如果您对某个对象有所了解,并且以后需要检索该知识,请不要丢弃该知识。如果以后需要了解一些信息,请记住。

如果希望将整数和对象安全地存储到一个stringlist中,请定义一个变量容器类来保存整数或对象

下面是一个大致概述的类,包括一个测试项目

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.


正如@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)
等故意无效的指针肯定会导致访问冲突错误。因此,您可以初步检查指针的较高
单词
s是否为零(与宏中的逻辑相同:


最好的解决方案是不要将两者混用。如果要存储对象,请存储对象,然后可以将其与
nil
进行比较,或者使用
Assigned
。如果要存储整数,请存储整数(这就是
指针(7)的内容)
does-它不是存储指针,而是将整数类型转换存储为指针以通过编译器)。一般来说,您可以假设低于$FFFF的地址是伪指针,而不是对象,您没有对该内存范围的读取权限。唉,我找不到支持这一点的文档。这个问题暴露了好奇(好)或鲁莽(坏)。避免在实际代码中使用不安全或不完整的想法,否则会发生不好的事情。如果这是好奇心,就像我在这里假设的那样,那么思考这个问题是值得称赞的。为什么不坚持使用对象并做一个数据持有者呢?Tmyintegerdata…不混合是最好的想法,我同意。:-)我在外部代码部分遇到了这种用法,我只是想知道是否有一个很好的解决方案来解决这个“混合问题”,因此我很好奇。谢谢大家在释放
TStringList
之前,不要忘记释放
TVariantContainer
对象(除非将其
OwnsObjects
属性设置为True-XE2+),否则会泄漏内存。是的,这种重构会起作用,但我想知道是否有一个好方法,无需进行此类更改即可快速检查。感谢您的建议。+1但您实际使用
变量
作为支持字段而不是
整数
指针
,这有什么原因吗?@LievenKeersmaekers,在内部使用一个变量或单独的整数,ToObject变量也会起作用。我使用变体的想法是,如果你想添加更多类型,变体也可以处理它们。@grafd,你可以走捷径,做出假设,但如果你想得到一个实解,就必须这样做。如果我可以轻松地将一个字符串列表一分为二,那么这将是一种方法。谢谢
function Is_IntResource(lpszType: PChar): BOOL;
begin
  Result := ULONG_PTR(lpszType) shr 16 = 0;
end;