Nº
En primer lugar, no hay tal cosa como una "variable no instanciada." Lo instancia por el mero hecho de escribir su nombre y escribirlo en su archivo fuente.
En segundo lugar, ya sabes todo lo que hay que saber sobre una variable mirándola en tu código fuente. La variable deja de existir una vez que se compila su programa. Después de eso, todo es solo bits.
Un puntero solo tiene un tipo en tiempo de compilación. En tiempo de ejecución, todo lo que se puede hacer a esa dirección ya ha sido determinado. El compilador verifica eso, como ya anotó. Comprobar el tipo de variable en tiempo de ejecución solo es útil en los idiomas en los que el tipo de variable podría cambiar, como en los lenguajes dinámicos. Lo más cercano a Delphi es con su tipo Variant
. El tipo de variable siempre es Variant
, pero puede almacenar muchos tipos de valores en él. Para saber qué contiene, puede usar la función VarType
.
Cualquier momento que desee utilizar TypeInfo
para obtener la información de tipo del tipo asociado a una variable, también puede nombrar directamente el tipo que le interese; si la variable está dentro del alcance, puede buscar su declaración y usar el tipo declarado en su llamada al TypeInfo
.
Si desea pasar una dirección arbitraria a una función y hacer que esa función descubra la información del tipo por sí misma, no tiene suerte. En su lugar, deberá pasar el valor PTypeInfo
como un parámetro adicional. Eso es lo que hacen todas las funciones Delphi integradas. Por ejemplo, cuando llama al New
en una variable de puntero, el compilador inserta un parámetro adicional que contiene el valor PTypeInfo
para el tipo que está asignando. Cuando llama al SetLength
en una matriz dinámica, el compilador inserta un valor PTypeInfo
para el tipo de matriz.
The answer that you gave sugiere que usted está buscando algo diferente a lo que usted solicitó.Dada su pregunta, pensé que estabas buscando una hipotética función que podría satisfacer este código:
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;
Usemos el IsClass
and IsObject
functions from the JCL para construir esa función:
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;
Es obvio que no va a funcionar para S
o Instance
arriba, pero vamos a ver qué pasa con Obj
:
Info := GetVariableTypeInfo(@Obj);
Eso debería dar un acceso violación. Obj
no tiene ningún valor, por lo que IsClass
y IsObject
leerán una dirección de memoria no especificada, probablemente no una que pertenezca a su proceso. Usted solicitó una rutina que usaría la dirección de una variable como entrada, pero la mera dirección no es suficiente.
Ahora echemos un vistazo más de cerca cómo se comportan realmente IsClass
y IsObject
. Esas funciones toman un valor arbitrario y comprueban si el valor se parece a, podría ser un valor del tipo dado, ya sea objeto (instancia) o clase. Utilizar de esta manera:
// 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;
en cuenta que las funciones le dicen nada sobre las variables, sólo alrededor de los valores que poseen. Realmente no consideraría esas funciones, entonces, para responder la pregunta de cómo obtener información sobre una variable.
Además, ha dicho que todo lo que sabe sobre la variable es su dirección. Las funciones que encontraste no toman la dirección de una variable. Toman el valor de una variable. He aquí una demostración:
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;
Es posible oponerse a la forma en que estoy abusando de estas funciones haciendo pasar lo que es, obviamente, la basura en ellos. Pero creo que esa es la forma solo tiene sentido hablar sobre este tema. Si sabes que nunca tendrás valores basura, entonces no necesitas la función que estás pidiendo de todos modos porque ya sabes lo suficiente sobre tu programa para usar tipos reales para tus variables.
En general, usted está haciendo la pregunta incorrecta. En lugar de preguntar cómo determina el tipo de una variable o el tipo de un valor en la memoria, debe preguntar cómo se metió en la posición en la que aún no conoce los tipos de sus variables y sus datos.
Puede tener "procedure Test (var AUntypedParam)" (o con "const" o "out" en lugar de "var"). –
Lo siento. Mi respuesta debería haber dicho "parámetro sin tipo" en lugar de "puntero sin tipo". Corregido –