Ir para conteúdo
  • Cadastre-se

dev botao

Objetos: Saber se é a mesma instância - Chupa essa manga


Ver Solução Respondido por Daniel Simoes,
  • Este tópico foi criado há 1718 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Recommended Posts

Postado (editado)

Olá estou quebrando a cabeça.

Preciso armazenar um objeto e depois comparar se é ele mesmo. parece simples, mas tem umas pegadinhas, veja abaixo:

var
  A, B: TObject;
begin
  A := TObject.Create;
  B := A;

  A.Free;
  A := TObject.Create;
  // Agora A é outro objeto

  ShowMessage(             
    BoolToStr(A = B, True)
    );
 
//Exibe "True"

end;

Como assim?  Eu sei... O operador "=" apenas compara se os endereços na memória são iguais, e como  variáveis são ponteiros pra endereços de memória, deve ser porque o mesmo endereço onde  A estava armazenado é "aproveitado" para armazenar o novo objeto.

Mais uns testes:

var
  A, B, C: TObject;
begin

  A := TObject.Create;
  B := A;

  A.Free;

  C := TObject.Create;
  //C é outro objeto

  ShowMessage(             
    BoolToStr(A = C, True) 
    ); //Exibe "True"

  ShowMessage(             
    BoolToStr(Pointer(A) = Pointer(C), True) 
    ); //Exibe "True"

  ShowMessage(             
    BoolToStr(A.Equals(C), True) 
    );  //Exibe "True"

end;

Mas e agora? Usei variáveis diferentes(A e C), continua sendo o mesmo endereço.  O objeto C é criado no mesmo endereço onde estava armazenado A.

Os objetos são iguais, mas não são a mesma instância.

Preciso saber se é a mesma instância. 

Tem alguma maneira sem ter que dar um B:=nil, visto que onde destruo A não tenho acesso a B, e se possível sem ter que alterar a classe original, adicionando uma referencia ou algo do tipo.

Editado por Delcio
Postado (editado)
28 minutos atrás, Daniel Simoes disse:

no FPC existe TObject.Equals

mas a ideia é semelhante...

Não testei no FPC, mas no delphi tem também e realmente é a mesma coisa que "="; 

TObject tem GetHashCode, mas também retorna o mesmo valor;

Editado por Delcio
  • Fundadores
  • Solution
Postado

Acho que a melhor solução.... seria trocar:

A.Free;

por

FreeAndNil(A);

pois nesse caso,  "A" deixará de apontar para a memória (que foi liberada)

  • Curtir 1
Consultor SAC ACBr

Daniel Simões de Almeida
O melhor TEF, é com o Projeto ACBr - Clique e Conheça
Ajude o Projeto ACBr crescer - Assine o SAC

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

  • Consultores
Postado
1 hora atrás, Delcio disse:

Como assim?  Eu sei... O operador "=" apenas compara se os endereços na memória são iguais, e como  variáveis são ponteiros pra endereços de memória, deve ser porque o mesmo endereço onde  A estava armazenado é "aproveitado" para armazenar o novo objeto.

Nessa comparação os objetos são os mesmos. Não entendi sua dúvida. A e B são o mesmo objeto no mesmo endereço. O resultado True está correto.

1 hora atrás, Delcio disse:

.  O objeto C é criado no mesmo endereço onde estava armazenado A.

Os objetos são iguais, mas não são a mesma instância.

Você deu Free no objeto A. Liberou a memória indicando que ela pode ser utilizada por outro objeto. Como o objeto C é instanciado depois da liberação da memória, ele pode usar o mesmo endereço. O resultado vai ser igual.

Usar FreeAndNil vai ser a solução mais simples pro seu problema.

Mas sinceramente, acho que você está indo num caminho incorreto. Me parece que há violação de princípios de orientação a objetos. Seu código não deveria precisar desse tipo de comparação.

O que você está realmente tentando fazer? Que problema você está tentando resolver?

 

 

  • Curtir 2

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
Postado
6 minutos atrás, EMBarbosa disse:

Nessa comparação os objetos são os mesmos. Não entendi sua dúvida. A e B são o mesmo objeto no mesmo endereço. O resultado True está correto.

Os objetos são "iguais", mas não deveriam ser o "mesmo", pois são instancias diferentes. Isso causa confusão e foi motivo de um bug em nosso sistema.

24 minutos atrás, Daniel Simoes disse:

FreeAndNil(A);

Também pensei em FreeAndNil, mas tem que ser em B, o problema é que não tenho acesso a B onde libero A. Veja:

var
  A, B: TObject;
begin
  A := TObject.Create;
  B := A;

  FreeAndNil(A);
  A := TObject.Create;
  // Agora A é outro objeto


  ShowMessage(             
    BoolToStr(A = B, True) 
    ); //True
end;
9 minutos atrás, EMBarbosa disse:

O que você está realmente tentando fazer? Que problema você está tentando resolver?

Preciso armazenar um determinado objeto e depois, em outro local da aplicação preciso verificar se ele continua sendo o mesmo, não se é igual, mas se é a mesma instancia anterior.

Já contornei o problema original, mas não achei a solução ideal, deveria haver algo como TObject.SameInstance, já que o operador "=" só verifica se  o ponteiro das duas variáveis é o mesmo, e sim ele é o mesmo, mas os objetos não,  já que o primeiro foi destruído e  e criado novamente. 

Mas eu intendi, o operador é de igual,  e não de "mesmo": Dois carros iguais não significa que são o mesmo carro.

Mas achei interessante deixar este problema exposto para que outros não caiam nesta "arapuca".

Resolvi mais ou menos assim:

type
  TTest = class
  private
    FVar: Pointer;
  public
    constructor Create(var CheckInstanceVar); virtual;
    destructor Destroy; override;
  end;

constructor TTest.Create(var CheckInstanceVar);
begin
  inherited Create;
  FVar := @CheckInstanceVar;
end;

destructor TTest.Destroy;
begin
  inherited;
  if FVar = Self then
    FVar := nil;
end;

...

var
  A, B, C: TTest;
begin
  A := TTest.Create(B);

  A.Free;
  A := TTest.Create(C);
  // Agora A é outro objeto

  ShowMessage(             //
    BoolToStr(A = B, True) //
    );

end;

 

  • Fundadores
Postado
9 minutos atrás, Delcio disse:

Também pensei em FreeAndNil, mas tem que ser em B, o problema é que não tenho acesso a B onde libero A. Veja:

var
  A, B: TObject;
begin
  A := TObject.Create;
  B := A;

  FreeAndNil(A);
  A := TObject.Create;
  // Agora A é outro objeto


  ShowMessage(             
    BoolToStr(A = B, True) 
    ); //True
end;
 

Isso parece errado... você não deveria reaproveitar variáveis, dessa maneira...

Me parece que o seu modelo de codificação não está definindo muito bem as responsabilidade de cada método... O método que cria o objeto, deveria ser o mesmo responsável por liberá-lo ...

Consultor SAC ACBr

Daniel Simões de Almeida
O melhor TEF, é com o Projeto ACBr - Clique e Conheça
Ajude o Projeto ACBr crescer - Assine o SAC

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Postado
16 minutos atrás, Daniel Simoes disse:

Isso parece errado... você não deveria reaproveitar variáveis, dessa maneira..

Usei esse código apenas como exemplo, no meu caso nem reaproveito a variável, mas o erro acontece de forma semelhante ao exposto, pois o endereço da memoria do objeto que foi armazenado para posterior comparação é ocupado pelo novo objeto, e quando comparo usando "=" retorna True, mesmo sendo uma nova instância, pois aponta para o mesmo endereço da instancia anterior. 

No meu caso  a destruição/criação do objeto depende de fatores que não posso controlar, como a interação do usuário.

Resolvi armazenando uma referência para comparação no próprio objeto; 

  • Fundadores
Postado
43 minutos atrás, Delcio disse:

No meu caso  a destruição/criação do objeto depende de fatores que não posso controlar, como a interação do usuário.

Se você fala de Threads... é comum usar FreeOnTerminate, para que ela mesma faça a limpeza... nesse caso, o endereço de memória onde ela está não é importante...

Consultor SAC ACBr

Daniel Simões de Almeida
O melhor TEF, é com o Projeto ACBr - Clique e Conheça
Ajude o Projeto ACBr crescer - Assine o SAC

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

  • Consultores
Postado
1 hora atrás, Delcio disse:

", pois são instancias diferentes. Isso causa confusão e foi motivo de um bug em nosso sistema.

Acho que você não entendeu essa parte ainda. A e B são a mesma instância. São variáveis diferentes apontando pro mesmo objeto, mesmo endereço de referência.

1 hora atrás, Delcio disse:

", pois são instancias diferentes. Isso causa confusão e foi motivo de um bug em nosso sistema.

Nesse caso B pode continuar apontando para A  dependendo das opções do compilador. Veja:


type
  TMyClass = class(TObject)
  private
    { private declarations }
    Fs: string;
  end;

var
  A, B: TMyClass;
begin
    A := TMyClass.Create;
    B := A;

    A.Fs := 'Sou A';

    ShowMessage(B.Fs);

    FreeAndNil(A);

     A := TMyClass.Create;
      A.Fs := 'Sou NOVO A';
  // Agora A é outro objeto


  ShowMessage(
    BoolToStr(A = B, True)
    ); //True

    ShowMessage(B.Fs);
end.

 

1 hora atrás, Delcio disse:

Preciso armazenar um determinado objeto e depois, em outro local da aplicação preciso verificar se ele continua sendo o mesmo, não se é igual, mas se é a mesma instancia anterior.

Continuo sem saber o que você quer fazer. Você está explicando qual a implementação quer alcançar e não o objetivo...

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
Postado
1 hora atrás, EMBarbosa disse:

Acho que você não entendeu essa parte ainda. A e B são a mesma instância. São variáveis diferentes apontando pro mesmo objeto, mesmo endereço de referência.

Entendi sim,  as duas variáveis estão apontando para o mesmo endereço, e é justamente isso que causa a confusão, no final do código as duas variáveis estão armazenando o mesmo objeto.

Exatamente isso que impede eu comparar se "A" é igual a instância que eu tinha anteriormente e referenciei em "B".

O que eu precisava era uma forma de saber se meu "A" é a mesma instância de antes. Não me atentei a esse detalhe de endereçamento, e isso causou o problema;

Resumindo: Uma variável armazena uma referência a um endereço, e não o objeto em si.

 

  • Consultores
Postado
3 horas atrás, Delcio disse:

Resumindo: Uma variável armazena uma referência a um endereço, e não o objeto em si.

Bem, isso sempre foi assim... mas se você não sabia, posso entender sua confusão...

6 horas atrás, Delcio disse:

Resolvi mais ou menos assim:

Esse código me parece incorreto...  Você não colocou o "B:= A" e por isso o código vai dar false...

Ainda não consegui entender o que você quer fazer, mas não me parece que esse código realmente vai resolver seu problema...

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
Postado
3 horas atrás, EMBarbosa disse:

Esse código me parece incorreto...  Você não colocou o "B:= A" e por isso o código vai dar false...

Verdade, acabei não testando o código de exemplo antes de postar e esqueci de umas coisas;

O correto é esse:

type
  TTest = class
  private
    FVar: Pointer;
  public
    constructor Create(var CheckInstanceVar); virtual;
    destructor Destroy; override;
  end;

constructor TTest.Create(var CheckInstanceVar);
begin
  inherited Create;
  FVar := @CheckInstanceVar;
end;

destructor TTest.Destroy;
begin
  if TTest(FVar^) = Self then
    Pointer(FVar^) := nil;
  inherited;
end;

...
var
  A, B: TTest;
begin
  A := TTest.Create(B);

  B:= A;

  A.Free;
  A := TTest.Create(B);
  // Agora A é outro objeto

  ShowMessage(             //
    BoolToStr(A = B, True) //
    );

end;

Basicamente armazeno um ponteiro para a variável no objeto para que possa setar a variável para nil ao destruir o objeto;

Lembrando que esse código é somente um exemplo, usei esse artifício em uma implementação bastante complexa, onde A pode ser destruído e criado indiscriminadamente e depois preciso saber se A continua sendo a mesma instância do início;

  • Consultores
Postado
16 horas atrás, Delcio disse:

armazeno um ponteiro para a variável no objeto para que possa setar a variável para nil ao destruir o objeto;

Pra mim continua com problemas de design.

  • O que você fez foi um FreeAndNil em A;
  • O Create de A tem acesso a B que nem foi criado ainda;
  • B continua podendo gerar Access Violations depois de A ser destruído;

Mas se resolveu seu problema...

 

[]'s

Consultor SAC ACBr

Elton
Profissionalize o ACBr na sua empresa, conheça o ACBr Pro.

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.

Um engenheiro de Controle de Qualidade(QA) entra num bar. Pede uma cerveja. Pede zero cervejas.
Pede 99999999 cervejas. Pede -1 cervejas. Pede um jacaré. Pede asdfdhklçkh.
  • Este tópico foi criado há 1718 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar Agora
×
×
  • Criar Novo...

Informação Importante

Colocamos cookies em seu dispositivo para ajudar a tornar este site melhor. Você pode ajustar suas configurações de cookies, caso contrário, assumiremos que você está bem para continuar.