Página 1 de 1

Montar Array de Objetos no Harbour

Enviado: 19 Jul 2017 08:54
por Cavalo Marinho
Bom dia Pessoal, Solicito ajuda dos mestres para solucionar esta questão
Estou desenvolvendo uma aplicação com uma boa parte em orientação a objeto, acontece que eu nao consigo fazer um array de objeto, estou postando uma pequena parte do código para melhor esclarecer:
O que Acontece: faço um loop na tabela e preencho o objeto, até aí tudo certo, pego este objeto e adiciono a lista criada, o problema é que quando vou ler a lista ela está toda preenchida com o ultimo objeto lido, ou seja preencho tudo certinho mas nao sei por que ao ir adicionando mais um objeto esta lista atualiza todos os seus itens com este ultimo objeto:
Exemplo: Li o primeiro JOSE DOS SANTOS, adicionei a lista, pra conferir mandei exibir tudo ok, li o segundo JOSE OLIVEIRA adicionei a lista, mandei exibir e aí os dois itens da lista está com JOSE OLIVEIRA.
Fui alem, em vez de preencher com o objeto eu preenchi direto com o nome do cliente, funciona certinho, acontece que eu preciso do objeto, pois tenho neste todos os dados do cliente
Não sei se fui claro, mas é isto que acontece.

Código: Selecionar todos

***********************************************************************
Function ObterEntidadesPorChave(cChave)
************************************************************************
private aLista := {}

oCliente := TCliente()    // Instancio o Objeto

cChave := alltrim(cChave)
select CLIE                   // Seleciono a tabela cliente
set order to 2
seek cChave
while(cChave == left(CLIE->CLNOME, len(cChave)) .and. !eof())
	cliente := oCliente:ObterRegistro(.F.) // Aqui eu obtenho o cliente
	MSGINFO('OBJ=' + cliente:clnome) // Para testar mandei exibir o dado do objeto, tudo certo até aqui
	aadd(aLista, cliente)  // Adiciono o objeto na Lista, 
	DbSkip(1)
enddo 

// introduzido para testar
FOR A = 1 TO LEN(aLista)
	msginfo('Cliente ' + str(A) + ' ' + aLista[A]:clnome) // Quando exibo todos os itens da lista está com o ultimo objeto inserido
next
return(aLista)

Montar Array de Objetos no Harbour

Enviado: 19 Jul 2017 10:33
por JoséQuintas
Convém melhorar a parte de variáveis, compilando usando -w3 -es2, já que está elevando o nível dos fontes.

Mas é elementar...
Sua lógica está furada.
Não está garantindo conteúdo único no do while.

E fonte muito feio por sinal.... rs

Código: Selecionar todos

PRIVATE aLista := {}
Pra que criar como PRIVATE? A compilação -e3 -es2 já reclamaria disto.
Use LOCAL

Código: Selecionar todos

oCliente := TCliente() 
Acostume com o modo correto: TCliente():New()

Código: Selecionar todos

dbSkip(1)
É realmente usar isso ao invés de SKIP ? é o dobro de caracteres pra digitar.

Código: Selecionar todos

return(aLista)
RETURN não é função. É comando !!!
Não é porque aceita errado que deve usar errado
RETURN aLista

Agora vamos ao problema:
Por padrão, array é passado por referência, e seu array é..... PRIVATE, a mesma variável e mesmo array o tempo todo.

E sinceramente, nem entendi seu uso de classe, parece uma classe dentro de outra classe inútil.
Um chute, que deve resolver seu problema, seria este:

Código: Selecionar todos

FUNCTION ObterEntdadesPorChave( cChave )

   LOCAL aLista := {}

   cChave := Alltrim( cChave )
   SELECT clie
   SET ORDER TO 2
   SEEK cChave
   DO WHILE cChave == Left( CLIE->CLNOME, len( cChave ) ) .AND. ! Eof()
      Aadd( aLista, TCliente():ObterRegistro( .F. ) )
      SKIP
   ENDDO

   Mostra( aLista )
   
   RETURN aLista

STATIC FUNCTION Mostra( aLista )

   LOCAL oElement

   FOR EACH oElement IN aLista
      MsgInfo( oElement:clNome )
   NEXT
   
   RETURN NIL
O que deixa clara a parte esquisita.
Não está criando um array de classes, e sim um array de retornos.
E se só usa o retorno.... pra que classe?

Montar Array de Objetos no Harbour

Enviado: 19 Jul 2017 10:52
por JoséQuintas
Complementando:

Se entendi direito, só está pegando o conteúdo do registro pra usar como classe.
Isso é facilitar erros de fonte.
Seria melhor criar #defines pra estrutura do arquivo e usar array, assim teria controle total no fonte sobre erros.
DESDE QUE COMPILANDO COM -w3 -es2

A mesma coisa, sem classe, e mais seguro: (pelo menos é a impressão que tenho)

Código: Selecionar todos

#define CLIENTE_NOME       1
#define CLIENTE_ENDERECO 2

FUNCTION Obter( cChave )

   LOCAL aLista := {}
   SEEK cChave
   DO WHILE cChave == clie->clNome  .AND. ! Eof()
      AAdd( aLista, { clie->clNome, clie->clEndereco } )
      SKIP
   ENDDO

   FOR EACH oElement IN aLista
      ? oElement[ CLIENTE_NOME ], oElement[ CLIENTE_ENDERECO ]
   NEXT

   RETURN aLista

Montar Array de Objetos no Harbour

Enviado: 19 Jul 2017 11:02
por JoséQuintas
Se não entendeu porque é mais seguro:
É o simples fato de a compilação já saber se está usando algo que não existe, o compilador poder conferir o seu fonte.

Com Classe, erro somente em run-time:

Código: Selecionar todos

#include "hbclass.ch"

PROCEDURE Main

   LOCAL oClasse

   oClasse := Classe():New()
   ? oClasse:Minhoca

CREATE CLASS Classe
   VAR Nome
   ENDCLASS
d:\temp>d:\cdrom\fontes\build\build.exe

d:\temp>hbmk2 *.prg *.rc -m -n -w3 -es2 -workdir=c:\temp
hbmk2: Processing environment options: -comp=mingw
hbmk2: Processing configuration: d:\harbour\bin\hbmk.hbc
Harbour 3.4.0dev (f8911388ba) (2017-06-04 18:03)
Copyright (c) 1999-2017, https://github.com/vszakats/harbour-core/
Compiling 'test.prg'...
Lines 725, Functions/Procedures 2
Generating C source output to 'c:\temp\test.c'... Done.
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2017
UPX 3.94w Markus Oberhumer, Laszlo Molnar & John Reiser May 12th 2017

File size Ratio Format Name
-------------------- ------ ----------- -----------
1044992 -> 393728 37.68% win32/pe test.exe

Packed 1 file.

d:\temp>test

Error BASE/1004 Message not found: CLASSE:MINHOCA
Called from __ERRRT_SBASE(0)
Called from CLASSE:ERROR(0)
Called from (b)HBOBJECT(0)
Called from CLASSE:MSGNOTFOUND(0)
Called from CLASSE:MINHOCA(0)
Called from MAIN(8)
d:\temp>

Com #define, erro já na compilação

Código: Selecionar todos

#define CLIENTE_NOME 1

PROCEDURE Main

   LOCAL oVar := {}

   ? oVar[ CLIENTE_MINHOCA ]
d:\temp>hbmk2 *.prg *.rc -m -n -w3 -es2 -workdir=c:\temp
hbmk2: Processing environment options: -comp=mingw
hbmk2: Processing configuration: d:\harbour\bin\hbmk.hbc
Harbour 3.4.0dev (f8911388ba) (2017-06-04 18:03)
Copyright (c) 1999-2017, https://github.com/vszakats/harbour-core/
Compiling 'test.prg'...

test.prg:7: warning W0001 Ambiguous reference 'CLIENTE_MINHOCA'

Montar Array de Objetos no Harbour

Enviado: 19 Jul 2017 16:15
por Claudio Soto
El problema está en que los objetos son como los array se crean por referencia y por lo tanto cada elemento de la matriz debe contener una instancia diferente del objeto de lo contrario todos los elementos apuntaran a la misma instancia del objeto.

oCliente := TCliente()

Esta sentencia debe ir dentro del while para que a cada registro se cree una nueva instancia del objeto.

Montar Array de Objetos no Harbour

Enviado: 19 Jul 2017 16:29
por JoséQuintas
O fonte confunde.

Não reparou em uma coisa:
O que ele coloca no array não é o objeto, é um retorno.
Acho que nem importa se o objeto está dentro ou fora do do while, nesse caso, mas só dá pra saber olhando o fonte da classe, se existe New() ou Init() fazendo alguma coisa.

Código: Selecionar todos

cliente := oCliente:ObterRegistro(.F.) 
   aadd(aLista, cliente) 

Montar Array de Objetos no Harbour

Enviado: 19 Jul 2017 18:36
por Cavalo Marinho
ok
Obrigado por responder Jose Quintas
Mas não resolveu o meu caso do jeito que eu pretendo, do jeito que voce comentou em um dos post eu ja tinha feito e é assim que está funcionando no momento
DO WHILE cChave == clie->clNome .AND. ! Eof()
AAdd( aLista, { clie->clNome, clie->clEndereco } )
SKIP
ENDDO
Veja bem, meu código pode até está feio, não ser dos melhores pois não sou tão bom programador como os mestes deste forum, mas eu acho que o array deve ter algum tipo de bug pois se eu faço assim como o codigo acima funciona normal sem problema, mas eu queria ir mais alem, Criei uma classe Pai chamada Entity onde representa uma tabela, já tenho muitos métodos implementados e funcionando, tais como obterRegistro() GravarRegistro, Pesquisar etc, daí quando instancio uma classe que herda desta classe pai já tenho tudo pronto e funcional, somente alguns métodos mais especifico da classe eu implemento na classe filha, acontece que agora eu sentir a necessidade de obter uma lista de um determinada tabela ou seja quero obter uma lista de um determinado objeto que representa uma linha da tabela, e no meu teste funciona ou seja quando eu obtenho o registro eu trago tudo certo o que acontece é que ao inserir na lista o ultimo sempre atualiza todas a linhas já adicionada na lista.
Exemplo:
obtenho o primeiro registro que satisfaça a minha condição:
JOSE DA SILVA, RUA X
adiciono na lista e mostro a lista com o objeto JOSE DA SILVA, RUA X
obtenho o segundo registro
JOSE DOS SANTOS, RUA B
adiciono a lista e mostro os dois itens da lista, os dois já está com JOSE DOS SANTOS, RUA B
obtenho o terceiro registro
JOSE CARLOS OLIVEIRA, RUA TESTE
adiciono a lista e mostro os três itens da lista, os três já está com JOSE CARLOS OLIVEIRA, RUA TESTE
Acredito eu que seja algum bug na implementação do vetor em memória, pois se passo uma matriz como o exemplo acima funciona normal, só não com objetos.

Montar Array de Objetos no Harbour

Enviado: 19 Jul 2017 19:06
por JoséQuintas
Ninguém sabe tudo, e muito menos eu.
Estou procurando ensinar o que sei.

array é SEMPRE passado por referência, ao passar um array entre funções, está passando a localização dele e não o conteúdo.
Precisa rever o seu uso de arrays, tanto nesse fonte quanto na classe.

Ao fazer assim: a:= { a, b } , está criando o array do ZERO, portanto não é contaminado com conteúdo anterior, por isso funciona.

Montar Array de Objetos no Harbour

Enviado: 19 Jul 2017 19:09
por JoséQuintas
Esqueci de mencionar:
A falta de declaração como variável local pode também trazer problemas, por uma rotina interferir em outra.
Pensa que está usando uma variável quando na verdade está usando outra que já existia.

Montar Array de Objetos no Harbour

Enviado: 20 Jul 2017 12:36
por JoséQuintas
Veja um teste interessante:

Isto mostra "A"

Código: Selecionar todos

PROCEDURE Main

   LOCAL teste := {}

   Funcao( teste )

   ? teste[ 1 ]

   RETURN

FUNCTION Funcao( teste )

   Aadd( teste, "A" )

   RETURN NIL

Isto gera erro em run-time.

Código: Selecionar todos

PROCEDURE Main

   LOCAL teste := {}

   Funcao( teste )

   ? teste[ 1 ]

   RETURN

FUNCTION Funcao( teste )

   teste := { "A" }

   RETURN NIL

Isto mostra "A"

Código: Selecionar todos

PROCEDURE Main

   LOCAL teste := {}

   Funcao( @teste )

   ? teste[ 1 ]

   RETURN

FUNCTION Funcao( teste )

   teste := { "A" }

   RETURN NIL
Até tem explicação: o CONTEÚDO da variável array é passado por referência por default, mas o conteúdo não a variável.
Ao atribuir o valor novo, está destruindo o vínculo dos valores do array.
Já passando por referência, é a variável por referência e não o conteúdo, então permanece o conteúdo.

Fico na dúvida se isso é bug ou é proposital, mas dá pra encontrar explicação.... rs
Melhor ficar usando por referência sempre, pra não ter dúvida se vai ou não trocar o conteúdo.

Este último detalhe eu descobri agora, não sabia disso.

Montar Array de Objetos no Harbour

Enviado: 12 Jun 2024 14:08
por deividdjs
tarde amigos !!

Código: Selecionar todos

FUNCTION CarregarContas()
   
   LOCAL aContas, dHoje, cliant 
    aContas := {}
    dHoje := DATE()

	 RECEBER->(ORDSETFOCUS("RECCX_18"))
	 RECEBER->(DBGOTOP())
	 do while RECEBER->cd_dtvenc < date() .and. RECEBER->cc_receb == ' ' .and. RECEBER->(!EOF())
		
	  	 if RECEBER->cc_codcli <> cliant 
          AADD(aContas, { receber->cc_codcli, receber->cd_dtvenc })
		 endif   
 		 cliant := RECEBER->cc_codcli 
		 RECEBER->(DBSKIP())
		
	 enddo

    Mostra_array(aContas)

    RETURN aContas
 
 //------------------------------------------------------------------------------------------------------------------

static FUNCTION Mostra_array( aContas )
   
	LOCAL oElement

   
   FOR EACH oElement IN aContas
	      alert( oElement:CC_CODCLI, oElement:CD_DTVENC )
   NEXT
   
   
   RETURN NIL
não consigo mostrar no alert o conteudo de aContas ... dá erro .. alguem sabe por qual motivo ??

Saludos,

Montar Array de Objetos no Harbour

Enviado: 12 Jun 2024 14:30
por JoséQuintas
Confundiu array com classe.
Adicionou dois elementos no array: oElement[1] e oElement[2]

Também pode usar hb_ValToExp( aContas ) ou hb_ValToExp( oElement )

Montar Array de Objetos no Harbour

Enviado: 12 Jun 2024 14:34
por deividdjs
show .. funcionou ! obrigado

Saludos,