¿Trabajas normalmente con Variants…?

No es que sea frecuente en mi caso, pero si mirais el código de los módulos que tenga en ese momento en producción, alguna variable que otra de este tipo aparecerá. Lo que no suele ser tan normal, es que utilize en procedimientos y funciones parametros de entrada del tipo: Array of Variant.

¿Qué por qué digo esto…?

El caso es que hoy he estado revisando unas lineas de código en un procedimiento, que hacían uso de este tipo de parámetros de tipo matricial. Todo había estado funcionando sin problemas. Simplificando un poco, por un lado estaba examinando el procedimiento que invocaba, y por otro, el que era invocado, en este caso el constructor de un formulario. Más o menos era así el primero, que tenía la responsabilidad de crear una ventana de edición de datos:

procedure TwndPartes.GoToFicha(const AOperacion: TOperacion);
var
   v: Variant;
begin
   v:= fDatos.PartesTrabajo.FieldByName('ID_PTRABAJO').AsInteger;
   main.frmMain.ShowModule('EDITPARTE', [v, AOperacion]);
end;

Y el segundo era el constructor de dicha ventana, donde se recibían el array de parametros necesarios en la creación e inicialización de la ficha:

constructor TwndEditParteTrabajo.CreateWithParams(AOwner: TComponent;
  AParams: Array of Variant);
begin
   inherited CreateWithParams(AOwner, AParams);
   FDatos:= TdmEditParteTrabajo.Create(self);
   fKey:= AParams[0];
   fOperacion:= AParams[1];
   fDatos.PrepararEdicionRegistro(fKey, fOperacion);
   dsGeneral.DataSet:= fDatos.EditParteTrabajo;
end;

Hasta aquí todo correcto. El “problema” venía cuando surgió la necesidad de comunicar ambos módulos, la lista que contenía los datos, y el detalle de cada registro. Necesitaba pasar una referencia a un objeto, concretamente al dataset vinculado al conjunto de datos maestro con la idea de enlazarlos finalmente a la ficha edición del detalle. Todo a través del parámetro AParams, que en este caso lo había definido como un array de Variants.

Para verlo con mas claridad, voy a abrir un pequeño modulo de pruebas y allí analizaremos la solución. Abrimos un nuevo proyecto…
ok…

Vamos a poner un boton, para ejecutar el código. Tambien ponemos un Listbox, para visualizar los resultados.

Veamos el módulo que hemos obtenido tras unos minutos (¡¡ojo!! que no se hace solo… que hay que teclearlo… 😀 ):

ficha

unit uPruebaVariants;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls;

type
  TObjetoMio = class(TComponent)
  private
    FNombre: String;
    procedure SetNombre(const Value: String);
  public
     property Nombre: String read FNombre write SetNombre;
  end;

  TForm1 = class(TForm)
    btnOk: TButton;
    lbxLista: TListBox;
    procedure btnOkClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure RecorrerLista(ALista: Array of Variant);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TObjetoMio }

procedure TObjetoMio.SetNombre(const Value: String);
begin
  FNombre := Value;
end;

{ TForm }

procedure TForm1.btnOkClick(Sender: TObject);
var
  MiObjeto: TObjetoMio;
  i: Integer;
begin
   //creamos un objetos cualquiera
   MiObjeto:= TObjetoMio.Create(self);
   MiObjeto.Nombre:= 'Mi objeto';
   i:= 265;    //un numero cualquiera
   //recorremos la lista
   RecorrerLista([Integer(MiObjeto), i])
end;

procedure TForm1.RecorrerLista(ALista: Array of Variant);
var
  i: Integer;
  FObjeto: TObjetoMio;
begin
   for i:= Low(ALista) to High(ALista) do begin
      case VarType(ALista[i]) of
         varInteger: begin
                       try
                          FObjeto:= TObjetoMio(Integer(ALista[i]));
                          lbxLista.Items.Add(FObjeto.Nombre);
                       except
                          lbxLista.Items.Add(IntToStr(ALista[i]));
                       end;
                     end;
      end;
   end;
end;

end.

Si ejecutamos esta pequeña aplicacion de pruebas vemos que básicamente, lo que hemos hecho es obtener la dirección de la referencia al objeto que deseamos introducir dentro del Variant. Para ello, nos servimos de un Cast, que aparece explicito en la linea:

RecorrerLista([Integer(MiObjeto), i])

Luego, dentro de este procedimiento, en su implementación, recorremos el Array y rehacemos el vínculo con el objeto y ejecutamos el método deseado.

¿Funcionar…?

Sí… sí, funciona. Claro…

Pero hay un pequeño detalle que es el que hace interesante al artículo y es el siguiente: Admitamos que es correcto lo que hemos hecho (que lo es!). Al hacer el casting al tipo Integer, en un futuro no vamos a ser capaces de diferenciar cuando estemos recibiendo un entero y cuando un objeto (al menos sin tener que usar un esquema del tipo [try except end]… Así que nuestro método no es tan perfecto como imaginábamos…

😦

Despues de enviar un correo a mi compañero Juan, diciendole que lo tenía todo resuelto, le voy a dar un pequeño disgusto… en fin…

Bueno… no nos rindamos…

Se me ocurre otra cosa pero la vamos a ver en la siguiente entrada…

😉

Descargar el codigo fuente:pruebaVariants1