PDA

Ver la Versión Completa : Asignar 'array property' con record.elemento


yapt
13-05-2010, 14:37:03
Bueno,

en primer lugar disculpas por el título. La verdad es que no sé ni como describirlo de una forma concreta.

Se trata de lo siguiente (adjunto código muy sencillo y completo que he preparado para la ocasión). Solo hay que crear un form y un botón y al compilar, veo un bonito:

E2064 Left side cannot be assigned to

El caso es que es una clase que tiene un field que es un record y, como tal, tiene elementos dentro del record.

La cuestión es cómo acceder, de forma directa, a los elementos DENTRO del propio record.

Bueno, creo que con el ejemplo se vé claramente. Y no puede ser muy dificil, porque hay muchos componentes que usan esto (aunque no con Records, sino con Sub-Clases).

Cualquier ayuda será bien recibida.

Gracias.



unit Unit1;

interface

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

type
TRecHijo = record
campo1: string;
campo2: Boolean;
end;

TPadre = class
strict private
FNumHijos: byte;
FHijos : array of TRecHijo;
function GetHijo(const Index: Integer): TRecHijo;
procedure SetHijo(const Index: Integer; const Value: TRecHijo);
published
property HijosCount: Byte read FNumHijos;
public
constructor Create;
destructor Destroy; override;
property Hijo[const Index: Integer]: TRecHijo read GetHijo write SetHijo;
end;

type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
private
Padre : TPadre;
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
Padre.Hijo[1].campo1 := 'Hola'; //<<<<----- Aqui falla.
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Padre := TPadre.Create;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
Padre.Free;
end;

{ TPadre }

constructor TPadre.Create;
begin
inherited;
FNumHijos := 10;
SetLength(FHijos, FNumHijos);
end;

destructor TPadre.Destroy;
begin
inherited;
end;

function TPadre.GetHijo(const Index: Integer): TRecHijo;
begin
result := FHijos[index];
end;

procedure TPadre.SetHijo(const Index: Integer; const Value: TRecHijo);
begin
FHijos[Index] := Value;
end;

end.

Lord Delfos
13-05-2010, 18:43:56
Ah... Es que los registros son medio HDP... :)

Verás, cuando uno llama a la propiedad izquierdo, Delphi va a llamar al GetHijo ¿Verdad? Bueno, pero GetHijo es una función, y la función devuelve una copia del registro original. Y esa copia es, por defecto, de sólo lectura. Ahí está el problema.

Si uno hace algo así:



type
TRec = record
campo1: string;
campo2: Boolean;
end;

type PRec = ^TRec; // <---------------------------

type TAlgo = class
private
FDatos: array of TRec;
function GetDato(const Indice: Integer): PRec;
procedure SetDato(const Indice: Integer; const Valor: PRec);
public
property Datos[const Indice: Integer]: PRec read GetDato write SetDato;
end;

function TAlgo.GetDato(const Indice: Integer): PRec;
begin
Result:= @FDatos[Indice];
end;

procedure TAlgo.SetDato(const Indice: Integer; const Valor: PRec);
begin
FDatos[Indice]:= Valor^;
end;

MiObjeto.Datos[3].Campo1:= 'Hola'; // <--- funciona



Esto funciona, pero ¿porqué? Bueno, porque ahora estás devolviendo un puntero al original, y como la dereferencia en los registros (el ^) es automática, pues termina andando sin que tengas que agregar el ^ al hacer la asignación.

¿Se entiende lo que digo? Espero que sí, sino pues... que te ayude otro. Digo, volvé a preguntar y trato de explicarlo mejor. :)

Saludongos.

roman
13-05-2010, 18:56:49
Probando. No hacer caso.

yapt
13-05-2010, 18:57:33
jejejejeje... Que buen humor. Da gusto... :p

Veo el código y lo entiendo (el código). Aunque se me escapa un poco el concepto.



Verás, cuando uno llama a la propiedad izquierdo
A que te refieres con la propiedad IZQUIERDO ?

Delphi va a llamar al GetHijo ¿Verdad? Bueno, pero GetHijo es una función, y la función devuelve una copia del registro original. Y esa copia es, por defecto, de sólo lectura

Ok, esto lo entiendo... pero entonces porque esto SI funciona ?:

procedure TForm1.Button1Click(Sender: TObject);
var
HolaHola : TRecHijo;
begin
Padre.Hijo[1] := HolaHola; //<<<<----- Esto de aquí... ???
end;

Porque tambien Padre.Hijo[1] llaman al GetHijo, no ?

Muchas gracias.... así da gusto...

:)

yapt
13-05-2010, 19:23:10
Bueno, en cualquier caso y por no incordiar mucho (pues ya lo tengo funcionando con los consejos de Lord Delfos) ya tengo lectura para esta noche:

http://docwiki.embarcadero.com/RADStudio/en/Properties

Lo voy a leer detenidamente.

Muchas gracias de nuevo.

Lord Delfos
13-05-2010, 20:48:57
A que te refieres con la propiedad IZQUIERDO ?


Eh... Quise decir cuando la propiedad es llamada del lado izquierdo de una asignación. Que es cuando se llama al gettter y no al setter (el GetHijo, vamos).


procedure TForm1.Button1Click(Sender: TObject);
var
HolaHola : TRecHijo;
begin
Padre.Hijo[1] := HolaHola; //<<<<----- Esto de aquí... ???
end;

Porque tambien Padre.Hijo[1] llaman al GetHijo, no ?


A ver, a ver. Según entiendo yo.


Padre.Hijo[1]:= AlgunRegistro;


Sería:


Temporal:= Padre.FHijo[1];
Temporal:= AlgunRegistro;
Padre.FHijo[1]:= Temporal;


Ahora:


Padre.Hijo[1].campo1:= 'Hola';


Sería:

Temporal:= Padre.FHijo[1];
Temporal2:= Temporal.campo1;
Temporal2:= 'Hola';
Padre.FHijo[1]:= Temporal;


¡Ajá! Pero si asignamos Temporal (no Temporal2) a FHijo[1], entonces perdemos la asignación a campo1. ¿no?

Por eso, el compilador se niega a aceptar hacer tal cosa. En otros lenguajes, como C#, esto está permitido, aunque el valor NO es asignado. Es decir, pasa lo mismo que en el ejemplo de arriba.

Porqué motivo el compilador no hace Padre.FHijo[1].campo1:= Temporal2, te preguntarás. Pues porque la propiedad de clase devuelve un registro, no un string. Es decir, el GetHijo devuelve todo un registro, una copia del original, y sobre esa copia vos querés cambiar el valor. Recordemos que la propiedad trata sobre un registro, así que el SetHijo supone que le vas a pasar todo el registro, no solamente un campo...

En tu ejemplo en el que asignás un registro completo, ahí no hay problemas porque, precisamente, la propiedad trabaja con un registro, así la definiste vos. No sé si se entiende...

O al menos eso es lo que yo entiendo. :)

yapt
13-05-2010, 21:16:05
No sé si se entiende...

Se entiende PERFECTO Lord Delfos. Y los ejemplos con sus desgloses.... muy didácticos.

Muchas gracias.