我们可以将字符串连同一些关联对象一起添加到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] 只是一个指针,这显然会导致访问冲突。 提前谢谢!
答案 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;