10

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

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 的信息,只传递它的地址?

4

3 回答 3

31

不。

首先,没有“非实例化变量”之类的东西。您只需键入其名称并将其键入源文件即可实例化它。

其次,通过在源代码中查看变量,您已经了解了有关变量的所有信息。编译程序后,该变量将不复存在。在那之后,一切都只是一点点。

指针仅在编译时具有类型。在运行时,可以对该地址执行的所有操作都已确定。正如您已经指出的那样,编译器会对此进行检查。在运行时检查变量的类型仅在变量类型可能改变的语言中有用,例如在动态语言中。最接近德尔福的是它的Variant类型。变量的类型总是Variant,但您可以在其中存储多种类型的值。要找出它的内容,您可以使用该VarType功能。

任何时候你想用来TypeInfo获取与变量关联的类型的类型信息,你也可以直接命名你感兴趣的类型;如果变量在范围内,那么您可以找到它的声明并在调用中使用声明的类型TypeInfo

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

您给出的答案表明您正在寻找与您所要求的不同的东西。鉴于您的问题,我认为您正在寻找一个可以满足此代码的假设函数:

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;

让我们使用JCL中的IsClassandIsObject函数来构建该函数:

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没有价值,因此IsClass两者IsObject都将读取一个未指定的内存地址,可能不属于您的进程。您要求一个使用变量地址作为输入的例程,但仅仅地址是不够的。

现在让我们仔细看看如何IsClass以及IsObject真正的表现。这些函数采用任意值并检查该值是否看起来可能是给定类型的值,无论是对象(实例)还是类。像这样使用它:

// 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;

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

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

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;

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

总的来说,你问错了问题。与其问你如何确定变量的类型或内存中的值的类型,不如问你是如何使自己处于不知道变量和数据类型的位置的

于 2009-02-16T18:54:47.990 回答
3

使用泛型,现在可以在不指定的情况下获取类型信息。某些用户表示以下代码无法编译而不会出现错误。从 Delphi 10 Seattle 版本 23.0.20618.2753 开始,它编译时没有错误,如下面的屏幕截图所示。

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.

在此处输入图像描述

于 2019-02-15T05:09:35.677 回答
2

从来没听说过。您可以获取关于类的已发布属性的 RTTI(运行时类型信息),但不能获取字符串和整数等“普通”变量。信息根本不存在。

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

于 2009-02-16T18:38:55.593 回答