如何知道var是什么类型的?

时间:2009-02-16 18:25:12

标签: delphi runtime typeinfo

TypeInfo(Type)返回有关指定类型的信息,有没有办法知道var的typeinfo?

var
  S: string;
  Instance: IObjectType;
  Obj: TDBGrid;
  Info: PTypeInfo;
begin
  Info:= TypeInfo(S);
  Info:= TypeInfo(Instance);
  Info:= TypeInfo(Obj);
end

此代码返回:

[DCC错误] Unit1.pas(354):E2133 TYPEINFO标准函数需要类型标识符

我知道非实例化的var只是一个指针地址。 在编译时,编译器解析并执行类型安全检查。

在运行时,有没有办法知道更多关于var的信息,只传递其地址?

3 个答案:

答案 0 :(得分:29)

没有

首先,没有“非实例变量”这样的东西。只需在源文件中键入其名称和类型即可实例化它。

其次,通过在源代码中查看变量,您已经知道所有关于变量的知识。编译程序后,变量不再存在。在那之后,它只是一点点。

指针在编译时只有一个类型。在运行时,已经确定了可以对该地址执行的所有操作。正如您已经指出的那样,编译器会检查它。在运行时检查变量的类型仅在变量类型可能更改的语言中有用,如在动态语言中。最接近Delphi的是Variant类型。变量的类型始终为Variant,但您可以在其中存储多种类型的值。要了解它的含义,您可以使用VarType函数。

每当您想要使用TypeInfo获取与变量关联的类型的类型信息时,您也可以直接命名您感兴趣的类型;如果变量在范围内,那么您可以在调用TypeInfo时找到其声明并使用声明的类型。

如果你想将一个任意地址传递给一个函数并让该函数发现它自己的类型信息,那你就不走运了。您需要将PTypeInfo值作为附加参数传递。这就是所有内置Delphi函数的功能。例如,当您在指针变量上调用New时,编译器会插入一个附加参数,该参数包含您要分配的类型的PTypeInfo值。在动态数组上调用SetLength时,编译器会为数组类型插入PTypeInfo值。

The answer that you gave表示您正在寻找除您要求之外的其他内容。鉴于你的问题,我认为你正在寻找一个能满足这个代码的假设函数:

var
  S: string;
  Instance: IObjectType;
  Obj: TDBGrid;
  Info: PTypeInfo;
begin
  Info:= GetVariableTypeInfo(@S);
  Assert(Info = TypeInfo(string));

  Info:= GetVariableTypeInfo(@Instance);
  Assert(Info = TypeInfo(IObjectType));

  Info:= GetVariableTypeInfo(@Obj);
  Assert(Info = TypeInfo(TDBGrid));
end;

让我们使用IsClass and IsObject functions from the JCL来构建该功能:

function GetVariableTypeInfo(pvar: Pointer): PTypeInfo;
begin
  if not Assigned(pvar) then
    Result := nil
  else if IsClass(PPointer(pvar)^) then
    Result := PClass(pvar).ClassInfo
  else if IsObject(PPointer(pvar)^) then
    Result := PObject(pvar).ClassInfo
  else
    raise EUnknownResult.Create;
end;

它显然不适用于上面的SInstance,但让我们看看Obj会发生什么:

Info := GetVariableTypeInfo(@Obj);

这应该会导致访问冲突。 Obj没有任何价值,因此IsClassIsObject都将读取未指定的内存地址,可能不属于您的进程。您要求使用变量的地址作为输入的例程,但仅仅是地址是不够的。

现在让我们仔细看看IsClassIsObject的真实行为。这些函数采用任意值并检查值是否它可能是给定类型的值,对象(实例)或类。像这样使用它:

// This code will yield no assertion failures.
var
  p: Pointer;
  o: TObject;
  a: array of Integer;
begin
  p := TDBGrid;
  Assert(IsClass(p));

  p := TForm.Create(nil);
  Assert(IsObject(p));

  // So far, so good. Works just as expected.
  // Now things get interesting:

  Pointer(a) := p;
  Assert(IsObject(a));
  Pointer(a) := nil;
  // A dynamic array is an object? Hmm.

  o := nil;
  try
    IsObject(o);
    Assert(False);
  except
    on e: TObject do
      Assert(e is EAccessViolation);
  end;
  // The variable is clearly a TObject, but since it
  // doesn't hold a reference to an object, IsObject
  // can't check whether its class field looks like
  // a valid class reference.
end;

请注意,这些函数告诉您 nothing 有关变量的信息,而只是关于它们所持有的。那么,我不会真正考虑这些函数来回答如何获取变量类型信息的问题。

此外,你说你对变量的所有了解都是它的地址。您找到的函数不会获取变量的地址。他们获取变量的。这是一个演示:

var
  c: TClass;
begin
  c := TDBGrid;
  Assert(IsClass(c));
  Assert(not IsClass(@c)); // Address of variable
  Assert(IsObject(@c)); // Address of variable is an object?
end;

你可能会反对我如何通过将明显的垃圾传递给他们来滥用这些功能。但我认为这是唯一的方式,谈论这个话题是有意义的。如果你知道你永远不会有垃圾值,那么你不需要你要求的功能,因为你已经对你的程序有足够的了解,可以为你的变量使用真实的类型。

总的来说,你问的是错误的问题。不要问你如何确定变量的类型或内存中值的类型,你应该问你是如何进入你还不知道变量类型和数据的位置的< /强>

答案 1 :(得分:2)

不是我知道的。您可以在类的已发布属性上获取RTTI(运行时类型信息),但不能获取字符串和整数等“常规”变量等。信息根本不存在。

此外,在不传递类型的情况下传递var的唯一方法是使用泛型TObject参数,泛型类型(D2008,如),或者作为无类型参数。我想不出另一种传递它甚至可以编译的方式。

答案 2 :(得分:0)

使用泛型,现在无需指定即可获取类型信息。

program TypeInfos;
{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils, System.TypInfo;

type
  TTypeInfo = class
    class procedure ShowTypeInfo<T>(const X: T);
  end;

{ TTypeInfo }

class procedure TTypeInfo.ShowTypeInfo<T>(const X: T);
var
  LTypeInfo: PTypeInfo;
begin
  LTypeInfo := TypeInfo(T);
  WriteLn(LTypeInfo.Name);
end;

var
  L: Exception;
  B: Boolean;
begin
                             // Console output
  TTypeInfo.ShowTypeInfo(L); // Exception
  TTypeInfo.ShowTypeInfo(B); // Boolean
end.