Saudações, leitores!
Nas versões mais antigas do Delphi (como a bem conhecida versão 7), sempre que era necessário percorrer uma lista, utilizávamos uma variável do tipo integer
como contador, correto? No entanto, alguns programadores que migraram para as versões mais recentes do Delphi não conhecem algumas alternativas para essa codificação. As dicas desse artigo são simples, porém, podem despertar o interesse de programadores que ainda utilizam contadores. Vamos lá!
Introdução
Esse artigo é uma homenagem ao André Oliveira, que sempre orienta e recomenda melhores técnicas de programação através dos recursos que a linguagem fornece.
É bastante comum, em nossas codificações, percorrer listas para executar um processamento, como em itens de uma TListBox
, campos de um DataSet ou componentes de um formulário. Neste artigo, optei por exemplificar uma lista de objetos com Generics. Considere a seguinte classe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
type TPessoa = class private FNome: string; public constructor Create(const Nome: string); property Nome: string read FNome write FNome; end; implementation { TPessoa } constructor TPessoa.Create(const Nome: string); begin FNome := Nome; end; |
Nada de especial. A classe possui um campo private preenchido no constructor e manipulado através de uma propriedade. Como o exemplo é didático, suspendi a criação de Getters e Setters.
Na nossa aplicação, imagine que seja necessário trabalhar com uma lista de objetos do tipo TPessoa
.
1 2 |
var Lista: TObjectList<TPessoa>; |
Com essa situação delimitada, apresentarei três formas de iterar essa lista pelos seguintes motivos:
- Sair do paradigma de que só existe contadores;
- Apresentar os recursos presentes nas versões mais recentes do Delphi;
- Manter o código mais limpo e/ou expressivo.
1) Com contador
Bom, não preciso explicar muito, não é? :
A primeira forma consiste em utilizar uma variável numérica como contador da lista, acessando os itens pelo Ãndice da iteração atual. Muitos programadores utilizam essa forma por ser, podemos dizer, “tradicional”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var Lista: TObjectList<TPessoa>; Contador: integer; begin Lista := TObjectList<TPessoa>.Create; Lista.Add(TPessoa.Create('Walter White')); Lista.Add(TPessoa.Create('Frank Underwood')); Lista.Add(TPessoa.Create('Jack Bauer')); for Contador := 0 to Pred(Lista.Count) do ShowMessage(Lista[Contador].Nome); Lista.Free; end; |
Apenas a tÃtulo de observação, notaram a presença do Pred
? Essa função decrementa um número, substituindo, portanto, o Count - 1
.
2) Com a estrutura for-in
Depois que publiquei o artigo sobre a cópia de registros de um DataSet, algumas pessoas ficaram surpresas com o for-in no Delphi, principalmente pelo fato de que essa estrutura não existe na versão 7. Pois bem, o for-in pode ser interpretado como “para cada X em Y, faça…”, semelhante ao foreach do C# e Java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var Lista: TObjectList<TPessoa>; Pessoa: TPessoa; begin Lista := TObjectList<TPessoa>.Create; Lista.Add(TPessoa.Create('Walter White')); Lista.Add(TPessoa.Create('Frank Underwood')); Lista.Add(TPessoa.Create('Jack Bauer')); for Pessoa in Lista do ShowMessage(Pessoa.Nome); Lista.Free; end; |
3) Com um Enumerator
Este é um recurso que poucos conhecem. Com Enumerators, não há contadores ou estruturas for-in. É necessário utilizar uma estrutura while, acessando o item da iteração atual pela propriedade Current
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var Lista: TObjectList<TPessoa>; Itens: TEnumerator<TPessoa>; begin Lista := TObjectList<TPessoa>.Create; Lista.Add(TPessoa.Create('Walter White')); Lista.Add(TPessoa.Create('Frank Underwood')); Lista.Add(TPessoa.Create('Jack Bauer')); Itens := Lista.GetEnumerator; while Itens.MoveNext do ShowMessage(Itens.Current.Nome); Itens.Free; Lista.Free; end |
O diferencial do Enumerator é a abstração ao trabalhar com a lista. A função GetEnumerator
, por exemplo, responde a tipos genéricos, dispensando a necessidade de typecasts. Além disso, ao pressionar Ctrl + Espaço em Current
, observe também que o Code Completion já exibe a propriedade como tipo TPessoa
:
No caso de uma TListBox
ou similares que possuem uma lista de itens do tipo TStrings
, a sintaxe é ligeiramente diferente por não se tratar de Generics. Neste caso, utilizamos o TStringsEnumerator
:
1 2 3 4 5 6 7 |
var Itens: TStringsEnumerator; begin Itens := ListBox1.Items.GetEnumerator; while Itens.MoveNext do ShowMessage(Itens.Current); end; |
Conhece outra forma de percorrer uma lista? Compartilhe conosco nos comentários!
Até breve, pessoal!
Uia um artigo em minha homenagem rsrsrrs.
Parabéns André, excelente artigo, objetivo e claro como tem de ser =]
Merecido, Oliveira!
Agradeço pelas suas orientações e ideias para novos artigos! =)
Abraço!
Parabéns André, show, esperamos que você sempre continue compartilhando seus conhecimentos e experiencia conosco, seus artigos não só me ajudou muito como tenho certeza que deve ajudar muitos outros programadores de todos os nÃveis.
Abraço.
Obrigado, Daniel!
Sinto-me grato ao ler comentários como o seu. Pretendo continuar este trabalho por muito, muito tempo! 🙂
Agradeço a você também pelo apoio!
Abraço!
Parabéns André… conhecimento represado não é uma boa prática… quando esse é compartilhado todos ganham.
Excelente didática… Direto ao ponto.
Obrigado, Jocimar!
Eu que agradeço por toda a sua colaboração no blog e participação no Delphi-Ingá! Aprendo muito com você!
Abraço!
Bom dia, gostei deste artigo, não conhecia essas maneiras alternativas de percorrer lista ou objetos.
Mais um pouco de conhecimento adquirido…
Obrigado!
Esse é o objetivo, Fábio: compartilhar essas alternativas e os recursos relativamente desconhecidos do Delphi.
Obrigado pelo comentário e continue acompanhando o blog! Abraço!
São pessoas assim como você, André, que fazem a diferença! Conhecimento é poder! E compartilhá-los é de uma atitude muito nobre! Obrigada!
Grandes palavras, Ilara!
Agradeço pela visita e por ter deixado este comentário. 🙂
Abraços!
Excelente Artigo André!
Muito obrigado, Charles! 🙂
André boa tarde!
Tenho uma duvida com TStringList
Queria adicionar uma linha após uma linha x.
Ex: tenho um arquivo com 20 linhas, fiz a pesquisa e ache a linha que quero(linha 13), e após esta linha escrever uma nova linha sem apagar a próxima(linha14) e no final o arquivo terá 21 linhas.
Obrigado
Olá, Marcos, tudo bem?
Você pode usar o método
Insert
da própriaTStringList
. Este método permite inserir um texto em uma posição especÃfica, indicada no primeiro parâmetro. O segundo parâmetro é o texto a ser inserido.Abraço!
Uma duvida, quando libera a lista da memória com o Free, os objetos dentro dela também são liberados?
Olá, Eudécio, ótima pergunta!
A maioria das listas no Delphi possui uma propriedade chamada OwnsObjects, que determina se o gerenciamento de memória dos objetos será feito pela própria lista. Caso essa propriedade seja True, a lista se encarregará de liberar os objetos automaticamente. Por outro lado, se for False, a liberação dos objetos terá que ser feita manualmente.
Abraço!
Saudações André.
Tava lendo o texto, muita bacana explicação, parabéns. Me chamo Alessandro, sou iniciante no Delphi e tava procurado alguma luz num projetinho que eu estou desenvolvendo aqui, e iria adaptar o código abaixo se desse certo. Lendo as linhas do seu texto desta página do seu blog:
Ele me poupou de escrever aquele monte de linhas ShowMessage abaixo, testei com Memo e preencheu corretamente também. Não tenho pratica no StringGrid, você saberia dizer se daria certo apenas preencher uma UNICA LINHA do StringGrid (cada caractere numa coluna independente)? Por exemplo, uma string variável em quantidade de caracteres, aqui no caso 11 caracteres (“APARTAMENTO”), cada uma delas numa célula.
E para finalizar um loop com cada letrinha numa célula separada da mesma linha do StringGrid, começando na coluna 1 e ir repetindo até chegar na ultima coluna variável dessa linha.
A P A R T A M E N T O A P A R T A M E N T O A P A R T A M E N T O…
Olá, Alessandro, bom dia!
Para preencher cada célula do componente TStringGrid com uma letra de uma palavra, você pode usar o código abaixo como exemplo:
Abraço!
Olá André, bom dia, obrigado pelo retorno e pela sua ajuda.
Pena que ainda uso o Delphi 7, e o for in está presente apenas no Delphi 2005 pra frente, mas tá jóia, agradecido.
Abraços.
Olá, Alessandro!
No caso do Delphi 7, você pode usar a estrutura tradicional do For:
Abraço!
Olá André, que bacana. Agradeço muito a você, eu tava pelejando com monte coisa, Array, TList, Copy…
Desculpe amolação, só uma última pergunta se você poder me dizer, caso cada letra tiver um valor (variável) que representa o seu número de repetição dentro da Grid.
Por exemplo, a palavra “apartamento”:
Mostrar cada letra separada em cada célula, na mesma linha:
A|P|P|P|A|A|A|A|R|R|T|A|A|M|M|M|E|E|E|E|E|N|N|N|N|N|T|T|O
Obrigado.
Opa, Alessandro!
Neste caso você terá dois laços de repetição: um para a palavra, e outro para a quantidade de repetições de cada letra. Isso exige, claro, que você tenha também dois contadores. O código ficaria parecido com este:
Abraço!
Opa, André! kk
Agradeçido mesmo pela ajuda, precisando estamos à sua disposição.
Tenha bom dia e tudo de bom pra você e sua famÃlia e successo.
Bom dia!
Parabéns André, excelente artigo claro e objetivo.
Obrigado, EurÃpedes!
Parabéns pelos textos, aprendo muito com eles. Sou iniciante, você pode me dar uma orientação de como faço para concatenar numa contagem sequencial que é apresentado num memo, somente a cada virada de ano, data + o ano inicial de cada virada?
obrigada
01/01/2020 + 2020
02/01/2020
03/01/2020
04/01/2020
……….
31/12/2020
01/01/2021 + 2021
02/01/2021
03/01/2021
04/01/2021
……….
31/12/2021
01/01/2022 + 2022
02/01/2022
03/01/2022
04/01/2022
……….
31/12/2022
…………. continua
Olá, Juliana, bom dia!
Uma sugestão para essa rotina é o código abaixo (fiz a projeção para 3 anos). Faça a adaptação conforme a sua necessidade:
Abraço!
Parabéns pela forma simples e competente de como partilhar o conhecimento. Tenteis fazer com mais campos no TPessoa, tipo nome e telefone, mas na hora do ShowMessage exibe duas vezes o mesmo valor, no caso o nome “Pontes” , não exibe o “Matias” em seguinda o “Pontes”.
Olá, Matias, tudo bem?
O que acontece no seu código é que você está trabalhando com apenas uma instância da classe “TPessoa”.
Quando você preenche o nome como “Pontes”, na verdade, você está alterando o “Matias”, e não criando uma pessoa nova.
Para corrigir esse código, você pode criar uma nova instância antes de preencher os valores:
Espero ter ajudado!
Abraço!
Parabéns conteudo muito bom.
Tenho uma duvida, como eu posso utilizar para Condição de pagamento a TObjectList?
Olá, Fabiana.
Não tenho certeza se compreendi muito bem a sua dúvida, mas se você já tiver uma estrutura para condições de pagamento (por exemplo, “TCondicaoPagamento”), basta utilizá-la em cconjunt com a classe TObjectList, da mesma forma como foi feito nesse artigo para a classe “TPessoa”.
Abraço.
Cara, se alguém conseguir ler todos os seus artigos, no final, ele vai ser um programador Super Sênior!! Parabéns pelo trabalho e sucesso!!!
Opa, Geovani, muito obrigado pelo feedback!
Grande abraço!
Fala André, show de bola seu artigo, programo há algum tempo e estou desenvolvendo um projeto pessoal, onde tenho que desenhar como se fosse um CAD, já tive um bom avanço, mas gostaria de melhorá-lo e gostaria de contar com sua ajuda. Eu gostaria que quando eu criasse uma linha por exemplo que o comportamento da linha fosse como um objeto flutuante, igual quando se faz um relatório no quick report que vc pode arrastar os objetos para qualquer posição. Da forma que estou fazendo estou imprimindo a linha num Canvas, então não consigo fazer mais nada com ela a não ser apagar todo o canvas e gerar novamente. Não sei se consegui ser claro na minha dúvida. Já procurei e nao achei nada sobre isso, será que vc teria uma luz? Desde já agradeço.
Olá, Edvaldo, tudo bem?
Infelizmente não tenho conhecimento ou experiência avançados com manipulação gráfica no Delphi.
Mesmo assim, acredito que a melhor forma é buscar uma biblioteca gráfica de terceiros para o Delphi, que já disponibilize métodos para trabalhar com desenhos. Se você fizer tudo de forma nativa, vai ser bem complexo.
Dê uma olhada, por exemplo, no Skia4Delphi:
https://skia4delphi.org/
Ou então, busque por “Delphi Graphic Library” no Google. Aparecerá vários resultados, porém, não tenho propriedade para afirmar qual seria a melhor para a sua necessidade.
Abraço!
André eu vi mais uma forma de percorrer lista, me lembrou do c# até… olha…
Procedure TesteCarregar;
var Pessoa: TPessoa;
begin
for Pessoa in FLista do //Lista: TObjectList
ShowMessage(Pessoa.Nome);
end;
Olá, Rodrigo, tudo bem?
Acho que essa forma seria o item 2 do artigo, não?
Abraço!
Bom dia, Andre tudo bem?
Copiando o seu exemplo na integra, mesmo setando a propriedade OwnsObjects como true ainda causou o memory leak abaixo:
Lista := TObjectList.Create;
Lista.OwnsObjects := true;
Lista.Add(TPessoa.Create(‘Walter White’));
Lista.Add(TPessoa.Create(‘Frank Underwood’));
Lista.Add(TPessoa.Create(‘Jack Bauer’));
Itens := Lista.GetEnumerator;
while Itens.MoveNext do
Log(Itens.Current.Nome);
Lista.Free;
13 – 20 bytes: TList.TEnumerator x 1
Para resolver o problema eu fiz a seguinte tratativa: depois do Lista.Free eu acrescentei o Itens.Free;
Abraços.
Olá, Ronaldo.
MuitÃssimo obrigado por apontar essa falha. Eu também recebi o Memory Leak rodando o código.
Acabei de atualizar o artigo incluindo o Itens.Free.
Obrigado! Abraço.