如何知道 var 是什么类型?

发布于 2024-07-13 08:02:17 字数 454 浏览 7 评论 0原文

TypeInfo(Type) 返回有关指定类型的信息,有什么方法可以知道 var 的类型信息吗?

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

此代码返回:

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

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

在运行时,有没有办法只传递它的地址来更多地了解 var?

TypeInfo(Type) returns the info about the specified type, is there any way to know the typeinfo of a var?

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

This code returns:

[DCC Error] Unit1.pas(354): E2133 TYPEINFO standard function expects a type identifier

I know a non instantiated var is only a pointer address.
At compile time, the compiler parses and do the type safety check.

At run time, is there any way to know a little more about a var, only passing its address?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

壹場煙雨 2024-07-20 08:02:17

不。

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

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

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

让我们使用 IsClassIsObject 来自 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;

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

总的来说,你问了错误的问题。 你不应该问如何确定变量的类型或内存中值的类型,你应该问你如何让自己陷入还不知道变量和数据的类型的境地< /强>。

No.

First, there's no such thing as a "non-instantiated variable." You instantiate it by the mere act of typing its name and type into your source file.

Second, you already know all there is to know about a variable by looking at it in your source code. The variable ceases to exist once your program is compiled. After that, it's all just bits.

A pointer only has a type at compile time. At run time, everything that can be done to that address has already been determined. The compiler checks for that, as you already noted. Checking the type of a variable at run time is only useful in languages where a variable's type could change, as in dynamic languages. The closest Delphi comes to that is with its Variant type. The type of the variable is always Variant, but you can store many types of values in it. To find out what it holds, you can use the VarType function.

Any time you could want to use TypeInfo to get the type information of the type associated with a variable, you can also directly name the type you're interested in; if the variable is in scope, then you can go find its declaration and use the declared type in your call to TypeInfo.

If you want to pass an arbitrary address to a function and have that function discover the type information for itself, you're out of luck. You will instead need to pass the PTypeInfo value as an additional parameter. That's what all the built-in Delphi functions do. For example, when you call New on a pointer variable, the compiler inserts an additional parameter that holds the PTypeInfo value for the type you're allocating. When you call SetLength on a dynamic array, the compiler inserts a PTypeInfo value for the array type.

The answer that you gave suggests that you're looking for something other than what you asked for. Given your question, I thought you were looking for a hypothetical function that could satisfy this code:

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;

Let's use the IsClass and IsObject functions from the JCL to build that function:

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;

It obviously won't work for S or Instance above, but let's see what happens with Obj:

Info := GetVariableTypeInfo(@Obj);

That should give an access violation. Obj has no value, so IsClass and IsObject both will be reading an unspecified memory address, probably not one that belongs to your process. You asked for a routine that would use a variable's address as its input, but the mere address isn't enough.

Now let's take a closer look at how IsClass and IsObject really behave. Those functions take an arbitrary value and check whether the value looks like it might be a value of the given kind, either object (instance) or class. Use it like this:

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

Notice that the functions tell you nothing about the variables, only about the values they hold. I wouldn't really consider those functions, then, to answer the question of how to get type information about a variable.

Furthermore, you said that all you know about the variable is its address. The functions you found do not take the address of a variable. They take the value of a variable. Here's a demonstration:

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;

You might object to how I'm abusing these functions by passing what's obviously garbage into them. But I think that's the only way it makes sense to talk about this topic. If you know you'll never have garbage values, then you don't need the function you're asking for anyway because you already know enough about your program to use real types for your variables.

Overall, you're asking the wrong question. Instead of asking how you determine the type of a variable or the type of a value in memory, you should be asking how you got yourself into the position where you don't already know the types of your variables and your data.

海拔太高太耀眼 2024-07-20 08:02:17

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

输入图像描述这里

With generics, it is now possible to get the type info without specifying it.
Certain users indicated the following code doesn't compile without errors.
As of Delphi 10 Seattle, version 23.0.20618.2753, it compiles without errors, as seen below in the screenshot.

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.

enter image description here

嗼ふ静 2024-07-20 08:02:17

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

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

Not that I know of. You can get RTTI (Run Time Type Information) on published properties of a class, but not for "normal" variables like strings and integers and so forth. The information is simply not there.

Besides, the only way you could pass a var without passing a type is to use either a generic TObject parameter, a generic type (D2008, as in ), or as an untyped parameter. I can't think of another way of passing it that would even compile.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文