Как узнать, какой тип является вар?
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, как в), либо в качестве нетипизированного параметра. Я не могу придумать другой способ передать его, который бы даже компилировался.