Как узнать, какой тип является вар?

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

3 ответа

Нет.

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

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

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

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

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

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

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.

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

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

Другие вопросы по тегам