Как узнать, какой тип является вар? - PullRequest
9 голосов
/ 16 февраля 2009

TypeInfo (Type) возвращает информацию об указанном типе, есть ли способ узнать 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, только передавая его адрес?

Ответы [ 3 ]

29 голосов
/ 16 февраля 2009

номер

Во-первых, не существует такой вещи, как "неинстанцированная переменная". Вы создаете его, просто вводя его имя и вводя в исходный файл.

Во-вторых, вы уже знаете все, что нужно знать о переменной, посмотрев на нее в своем исходном коде. Переменная перестает существовать после компиляции вашей программы. После этого это всего лишь биты.

Указатель имеет тип только во время компиляции. Во время выполнения все, что можно сделать по этому адресу, уже определено. Компилятор проверяет это, как вы уже заметили. Проверка типа переменной во время выполнения полезна только в языках, где тип переменной может измениться, как в динамических языках. Наиболее близкий 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;

Давайте используем функции IsClass и IsObject из 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;

Очевидно, он не будет работать для S или Instance и выше, но давайте посмотрим, что происходит с 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;

Вы можете возразить против того, как я злоупотребляю этими функциями, передав в них то, что очевидно является мусором. Но я думаю, что это only , так что имеет смысл поговорить на эту тему. Если вы знаете, что у вас никогда не будет значений мусора, то вам все равно не нужна запрашиваемая вами функция, потому что вы уже достаточно знаете о своей программе, чтобы использовать реальные типы для ваших переменных.

В целом, вы задаете не тот вопрос. Вместо того, чтобы спрашивать, как вы определяете тип переменной или тип значения в памяти, вы должны спрашивать, как вы оказались в положении, когда вы еще не знаете типы ваших переменных и ваших данных .

2 голосов
/ 16 февраля 2009

Не то, что я знаю. Вы можете получить RTTI (информацию о типе времени выполнения) для опубликованных свойств класса, но не для «обычных» переменных, таких как строки и целые числа и так далее. Информации просто нет.

Кроме того, единственный способ передать переменную без передачи типа - это использовать универсальный параметр TObject, универсальный тип (D2008, как в) или в качестве нетипизированного параметра. Я не могу придумать другой способ передать его, который бы даже компилировался.

0 голосов
/ 15 февраля 2019

С помощью дженериков теперь можно получить информацию о типе, не указывая ее.

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.
...