【问题标题】:How to know what type is a var?如何知道什么是 var 类型?
【发布时间】:2009-02-16 18:25:12
【问题描述】:

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

【问题讨论】:

    标签: delphi runtime typeinfo


    【解决方案1】:

    没有。

    首先,不存在“非实例化变量”之类的东西。您只需将其名称输入到源文件中即可实例化它。

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

    指针在编译时只有一个类型。在运行时,可以对该地址执行的所有操作都已确定。正如您已经指出的那样,编译器会对此进行检查。在运行时检查变量的类型仅在变量类型可能改变的语言中有用,例如在动态语言中。最接近 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;
    

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

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

    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;
    

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

    总的来说,你问错了问题。与其问你如何确定变量的类型或内存中的值的类型,你应该问自己是如何进入不知道变量和数据类型的位置的 /strong>。

    【讨论】:

      【解决方案2】:

      使用泛型,现在可以在不指定的情况下获取类型信息。 某些用户表示以下代码无法正确编译。 从 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.
      

      【讨论】:

      • 错误:E2531 方法“ShowTypeInfo”需要显式类型参数
      • 在柏林,这不会编译并生成错误 E2531。不过,它在 Rio 和 Tokyo 下编译得很好。
      • 我有一段时间没见过@KenWhite了。很高兴看到评论。
      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-04
        • 2013-06-02
        • 1970-01-01
        • 2012-08-01
        • 2013-02-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多