Logo

Marco Cantù
Pascal Esencial

Capítulo 10 Actualizar
Variantes

Para proporcionar plena compatibilidad con OLE, la versión de 32 bits de Delphi incluye el tipo de datos variante (Variant). Aquí, quiero discutir este tipo de datos desde una perspectiva general. El tipo Variant tiene, de hecho, un efecto omnipresente en todo el lenguaje, y la biblioteca de componentes Delphi también lo usa de maneras no relacionadas con la programación OLE.

Los variantes no tienen tipo

En general, se pueden usar variantes para almacenar cualquier tipo de datos y realizar numerosas operaciones y conversiones de tipo. No pase por alto que esto contradice el estilo general del lenguaje Pascal y los buenos hábitos de programación. Los variantes son comprobados y computados durante la ejecución, no durante la compilación. El compilador no le advertirá de posibles errores en el código, que pueden ser descubiertos sólo probándolo un buen número de veces. Normalmente, se puede considerar que las porciones de código que usan variantes son código interpretado porque, como en tal código, muchas operaciones no pueden ser realizadas hasta la ejecución del programa. Esto afecta especialmente a la velocidad del mismo.

Ahora que les he advertido contra el uso del tipo Variant, es hora de echarle un vistazo a qué se puede hacer con él. Básicamente, una vez que ha declarado una variable variante como la siguiente ...

var
  V: Variant;

... puede asignarle valores de distintos tipos :

V := 10;
V := 'Hello, World';
V := 45.55;

Una vez que tenga el valor variante, puede copiarlo a cualquier tipo de datos - ya sea compatible o no. Si asigna un valor a un tipo de datos incompatible, Delphi realiza una conversión, si puede. Si no puede, muestra un error de ejecución. De hecho, un variante almacena la información de tipo junto a los datos, permitiendo cierto número de operaciones de ejecución. Estas operaciones pueden resultarle fáciles de manejar, pero son tanto lentas como inseguras.

Considere el siguiente ejemplo (llamado VariTest), que es una extensión del código que aparece arriba. Coloqué tres cajas de edición en un formulario nuevo, añadí unos cuantos botones, y escribí el siguiente código para el evento OnClick del primer botón:

procedure TForm1.Button1Click(Sender: TObject);
var
  V: Variant;
begin
  V := 10;
  Edit1.Text := V;
  V := 'Hello, World';
  Edit2.Text := V;
  V := 45.55;
  Edit3.Text := V;
end;

Es divertido, ¿no? Aparte de asignar un variante que contiene una cadena a la propiedad Text de un componente de edición, puede asignarle al Text un variante que contenga un entero o un número de coma flotante. Como puede ver en la Figura 10.1, funciona bien.

Figura 10.1: La salida del ejemplo VariTest después de pulsar el botón Assign.

Aún peor, se pueden usar variantes para calcular valores, como puede verse en el código correspondiente al segundo botón:

procedure TForm1.Button2Click(Sender: TObject);
var
  V: Variant;
  N: Integer;
begin
  V := Edit1.Text;
  N := Integer(V) * 2;
  V := N;
  Edit1.Text := V;
end;

Es arriesgado escribir este tipo de código - y esto es decir poco. Si la primera casilla de edición contiene un número, todo va bien. Si no, se produce una excepción. En fin, puede escribirse código de este tipo, pero no se lo aconsejo, a no ser que tenga una imperiosa razón para hacerlo. Mejor, cíñase a los tipos de datos tradicionales de Pascal y a la posibilidad de comprobación de código durante la compilación. En Delphi en la VCL (Visual Component Library), normalmente los variantes son usados sólo para proporcionar compatibilidad OLE y para acceder a campos en una base de datos.

Estudio en profundidad de los variantes

Delphi incluye un tipo de registro variante, TVarData, que tiene la misma distribución de memoria que el tipo Variant. Puede usarse para averiguar cuál es el auténtico tipo de un variante dado. La estructura TVarData incluye al tipo del variante, indicado por VType, algunos campos de reserva, y el valor auténtico.

Los valores posibles del campo VType se corresponden con los tipos de datos que pueden usarse en la automatización OLE, que, a menudo, son tipos OLE o tipos variantes. He aquí una lista alfabética completa de los tipos de variante disponibles:

Encontrará descripciones de estos tipos en el apartado "Values in variants", en la ayuda de Delphi.

Hay también muchas funciones que permiten operar sobre variantes, que puede utilizar para hacer conversiones de tipos específicas, o para pedir información sobre el tipo de un variante (tome como ejemplo la función VarType mencionada). La mayoría de estas funciones de conversión de tipo y asignación son, en realidad, llamadas automáticamente cuando se escribe expresiones usando variantes. Otras rutinas de apoyo a variantes operan, en verdad, en vectores variantes (véase el apartado "Variant support routines" en la ayuda de Delphi).

Los variantes son leeeentos

El código en que se hace uso del tipo Variant es lento, no sólo al convertir tipos de datos, sino también cuando se añaden dos valores de variante que contienen un entero, cada uno. ¡ Son casi tan lentos como el código interpretado de VisualBasic ! Para comparar la velocidad de un algoritmo basado en variantes con uno que, teniendo el mismo código, está basado en enteros, examine el ejemplo VSpeed.

Este programa ejecuta un bucle, contando su velocidad y mostrando el estado en una barra de progreso. Aquí está el primero de los dos bucles, muy similares, basado en enteros y variantes respectivamente :

procedure TForm1.Button1Click(Sender: TObject);
var
  time1, time2: TDateTime;
  n1, n2: Variant;
begin
  time1 := Now;
  n1 := 0;
  n2 := 0;
  ProgressBar1.Position := 0;
  while n1 < 5000000 do
  begin
    n2 := n2 + n1;
    Inc (n1);
    if (n1 mod 50000) = 0 then
    begin
      ProgressBar1.Position := n1 div 50000;
      Application.ProcessMessages;
    end;
  end;
  // we must use the result
  Total := n2;
  time2 := Now;
  Label1.Caption := FormatDateTime (
    'n:ss', Time2-Time1) + ' seconds';
end;

Merece la pena examinar el código de temporización, porque es algo que se puede adaptar fácilmente a cualquier prueba de rendimiento. Como se ve, el programa usa la función Now para averiguar la hora actual, y la función FormatDateTime para dar el tiempo transcurrido, pidiendo sólo los minutos ("n") y los segundos ("ss") en la cadena de formato. Puede usar en vez de esto la función GetTickCount del API de Windows, que devuelve una medida muy precisa de los milisegundos transcurridos desde que se inició el sistema operativo.

En este ejemplo la diferencia de velocidad es, de hecho, tan grande, que lo notará aun sin calcularlo sin precisión. De cualquier modo, puede ver los resultados de mi propia computadora en la Figura 10.2. En la práctica, los valores dependerán de la máquina en que ejecute el programa, pero la proporción no cambiará mucho.

Figura 10.2: Las velocidades respectivas de un mismo algoritmo, en sus versiones en enteros y variantes (el tiempo requerido, en realidad, depende de cada computadora), ejemplificado por VSpeed.


Conclusión

Los variantes son tan diferentes de los tipos de datos tradicionales de Pascal, que he decidido hablar de ellos en este breve capítulo aparte. Aunque su papel se restringe a la programación OLE, pueden ser útiles para escribir programas de forma rápida y chapucera, sin tener que plantearse nada sobre tipos de datos. Como hemos visto, esto afecta de lleno al rendimiento.

Ahora que hemos cubierto la mayor parte de las características del lenguaje Delphi, pasaremos a comentar la estructura general de un programa y la posibilidad de modularización que nos ofrecen las unidades.


Capítulo siguiente: Programas y unidades

© Copyright Marco Cantù, Wintech Italia Srl 1995-99
© Copyright de la traducción, Rafael Barranco-Droege, 2000