Post: [Recursos do Delphi] Class Operator

Alessandro Medeiros

Fala ai Radizeiros e Radizeiras, tudo bem com vocês?

No Delphi, é possivel alterar a maneira de como um operador trabalha(para tipos definidos pelo desenvolvedor).

Esse recurso no Delphi permite ao programador redefinir o significado de um operador, que é conhecimento como Class Operator.

Hoje no blog estarei falando sobre Class Operator, e eles nos permitem fazer coisas fantásticas no Delphi.

Class Operator existe na linguagem Delphi a muito tempo, só que eles eram específicos para record.

Com a evolução do Delphi esses Class Operator foram expandido para classes propriamente ditas.

Isso resultou no fato do Delphi compilar para arquitetura ARM, e dessa forma eles foram expandidos para classes, mas para o Windows, ele não funciona para classe.

Mas ele funciona com records, e consegue fazer chover com Class Operator.

Então vamos lá ao nosso primeiro exemplo, do poder do Classe Operator.

Possuo uma classe record, que possui um valor inteiro e um string.

Então vamos trabalhar com essa nossa classes record.

TProduto = record
    Valor : Integer;
    Nome : String;
end;
...
procedure TForm1.Button1Click(Sender: TObject);
var
    a, b, c : TProduto;
begin
    a.Valor := 10;
    b.Valor := 30;
    c := a + b;
    ShowMessage(IntToStr(c.Valor));
end;

Observe que eu tenho um Nome que é uma string e um Valor que é um inteiro.

Criei três variáveis do tipo record TProduto, onde a.valor recebe 10, então meu produto a vale 10, e o produto b recebe 30.

Logo abaixo estou informando que c irá receber a soma desses dois produtos, conseguiu entender e pegar qual é a questão?

Bom, a situação é a seguinte, primeiro essa implementação o compilador não teria que compilar,  se eu criar duas classes e tentar soma-las não irá funcionar, correto?

As classes não se somam, os records não se somam, mas nesse nosso exemplo estou solicitando para eles se somarem, e olha que interessante.

Eu mandei somando duas instâncias de uma classe, mas o correto seria eu somar os valores e não a classe.

...
c.valor := a.valor + b.valor;
...

Fazendo dessa forma teríamos a soma correta.

Mas no nosso exemplo estamos somando dois objetos que são uma instância da classe TProduto.

Não estou somando as propriedades da classe, e sim os objetos da instância da classe.

Logo em seguida eu estou mandando visualizar o resultado no valor de “c”.

Olha o que irá acontecer, primeiro que não é para compilar.

Mas observe que ele irá compilar, e não só irá compilar, que ao clicar no Add ele irá mostrar a soma de “a” e “b”.

Muito legal né?

Se você está nos acompanhando aqui no blog lembra que usamos as Class Operator na nossa série de generics.

E como não pode faltar conteúdos que venha agregar para todos vocês, estou lhe mostrando a utilização da Class Operator.

Usando Class Operator, implicitamente, conseguimos somar objetos.

Imagine se você tivesse o item da nota fiscal?

Na hora de fazer o total da nota, e você pegar todos os itens e mandar somar, você não precisa varrer todos os itens para ver o que tem que ser somado.

Você só vai somar o objeto, e já irá ter uma classe implícita que faz isso para você de forma automática.

Você observou que eu não estou chamando nenhuma classe, não chamo um método, e nada, simplesmente mandei dois objetos.

Porque que isso é possível?

Algo que devemos ter como primordial para o conhecimento é a documentação da linguagem.

E esse é um recurso que está disponível lá na documentação do Delphi que são os Class Operator http://docwiki.embarcadero.com/RADStudio/Rio/en/Operator_Overloading_(Delphi).

Existem vários operadores que permitem ser sobrecarregados do Delphi.

O que aconteceu nesse nosso exemplo, é que não é sobrecarregado um método, mas sim o operador.

No exemplo, nós sobrecarregamos os operadores do Delphi, para fazer uma função do Delphi, diferente daquilo que queríamos que ela fizesse.

Com isso passa ser possível fazer operações que antes não era possível.

Vamos ver como isso funciona.

Então temos uma classe record,  e dentro dessa classe record temos os Class Operator, o operador de adição, o Add, que está dentro da documentação do Delphi.

Esse é um dos diversos operadores que podemos utilizar.

Então como funciona isso dentro do Delphi.

TProduto
    Valor : Integer;
    Nome : String;
    class operator Add(a, b: TProduto) : TProduto;
...

Nessa implementação eu estou dizendo que ele recebe os dois tipos e retorna o novo tipo.

Então eu estou dizendo, que todas as vezes que alguém usar o operador Add, ou seja, o operador de soma, para somar dois objetos do tipo TProduto, esse método do código acima, será chamado.

E o que esse método está fazendo?

class operator TProduto.Add(a, b: TProduto): TProduto;
begin
    Result.Valor := a.Valor + b.Valor;
end;

Ela pega o resultado que irá ter da soma dos valores de “a” e “b” implicitamente.

Então para quem está desenvolvendo não precisa fazer esse cálculo, ele simplesmente irá fazer a soma de a e b, e aí pronto, implicitamente ele pega os valores e faz a forma, que seria “correta”, que a maioria das pessoas fazem hoje.

Muito legal né pessoal?

Dessa forma você pode fazer o seu Delphi somar a classe, ou subtrair a classe.

Por exemplo, você tem uma rotina de venda, que ela tem a lista de itens, faz todo o cálculo da venda.

Vamos dizer assim, TVenda + TIten, e implicitamente você irá pegar essa classe de itens que chegou preenchida e vai fazer o que tiver que fazer para adicionar uma venda, recalcular total e por aí vai.

Quando alguém cancelar uma venda, por exemplo, você irá fazer TVenda – (item removido), a classe vem implicitamente faz todo o cálculo e resulta para você.

Então você consegue deixar o código mais transparente, mais legível, para quem for trabalhar não precisa ficar usando um monte de typecasting na tela, não precisa fazer um monte de coisas, você encapsula as suas funcionalidades.

A segunda, e uma outra operação que temos dentro das Class Operator, é o Implicit.

procedure TForm1.Button3Click(Sender: TObject);
var
    a : TProduto;
begin
    a := '30';
    ShowMessage(IntToStr(a.Valor));
end;

O que acontece dentro desse botão?

Observe, eu tenho uma variável do tipo TProduto, que foi chamada de “a”, eu digo que “a” irá receber uma string de 30.

Prestou atenção neste código?

A variável está recebendo uma string, eu to dizendo que uma variável do tipo de uma classe record está recebendo uma string, só que eu não estou fazendo assim:

...
a.Valor := StrToInt('30');
...

Esse seria o correto, o natural da programação para fazer com que o valor string 30 seja adicionado a uma property inteira dentro da classe record.

Graças ao Class Operator, o record recebe uma string de 30.

E ele vai retornar o valor, vamos ver isso funcionando?

Viu que ele retornou tudo certinho e sem erro?

Observe que ele implicitamente está pegando esse valor, fazendo a conversão desse valor e jogando para dentro da propriedade que está recebendo o valor.

Mas você deve está se perguntando como isso está sendo feito.

Foi criado um outro Class Operator dentro dessa nossa classe record, que é o Implicit.

TProduto
    Valor : Integer;
    Nome : String;
    class operator Add(a, b: TProduto) : TProduto;
    class operator Implicit(a : String) : TProduto;
...
class operator TProduto.Implicit(a : String) : TProduto; 
begin 
    Result.Valor := StrToInt(a); 
end;

O que seria esse operador implícito, e o que ele faz?

Ele recebe um valor, de um tipo de objeto diferente, eu to dizendo que esse record agora é um record mutante.

Então eu tenho uma variável do tipo TProduto, teoricamente, ela só poderia receber objeto do mesmo tipo dela, mas graças ao Implicit eu posso fazer com que o TProduto receba qualquer tipo de variável, contando que ela tenha o Class Operator Implicit tratando esse tipo.

Se eu tentar ao invés de passar uma string e passar para ele um inteiro, o compilador irá gerar erro, pois estou tratando somente string.

Mas caso eu queira também tratar isso, basta criar um outro Class Operator Implicit, recebendo por parâmetro um tipo inteiro.

...
class operator Implicit(a : Integer) : TProduto;
...
class operator TProduto.Implicit(a : Integer) : TProduto;
begin
    Result.Valor := a;
end;
...
procedure TForm8.Button3Click(Sender: TObject);
var
    a : TProduto;
begin
    a := 30;
    ShowMessage(IntToStr(a.Valor));
end;

Observe que agora estou passando para a variável “a” um inteiro.

Viu que não tem erro nenhum, na compilação, e claro na execução do exemplo.

Isso acontece pelo fato de estar informando para o compilador que tenho um método que trata valores inteiros também.

Quando chegar no objeto TProduto um valor inteiro, ele irá chamar implicitamente o método criado, e fazer o que tem que ser feito.

Essa é uma outra funcionalidade do Class Operator.

Isso é muito legal, porque, se passar para ele um inteiro ou uma string o compilador irá compreender e tratar isso, sem gerar erro.

Muito legal né?

Temos também um outro recurso sobre Class Operator, que se chama positive, que em algumas linguagens, na hora de fazer um incremento, simplesmente adicionamos o sinal de “+” na frente de uma variável.

E com Class Operator você pode sobrecarregar os operadores de incremento e decremento.

procedure TForm1.Button3Click(Sender: TObject);
var
    Prod : TProduto;
begin
    Prod := 10;
    Prod := +Prod;
    ShowMessage(IntToStr(Prod.Valor));
end;

Observe que possuo uma variável do tipo TProduto, dizendo que o prod está recebendo 10, e logo em seguida passando para o prod um incremento, ou seja, +prod.

Geralmente qual seria o correto para esta ação?

...
Prod.Valor := Prod.Valor + 1;
...

Esse seria o normal, e claro, é o que muito de nós que programamos em Delphi estamos acostumados.

Só que agora estou desmistificando isso da seguinte forma, estou dizendo que o objeto de TProduto, não a propriedade valor de TProduto, e sim um objeto, está recebendo um objeto com o operador de adição, ou seja, fazendo o incremento no objeto.

Nesse ponto interceptamos o operador, também com Class Operator, para tratarmos o que queremos que aconteça quando essa ação acontece.

Muito legal né?

Vamos dizer que você tem sua classe de itens, e o número dos itens são incrementados, todas as vezes você vai adicionar um item vai colocando, item1, item2…

Você não precisa ficar colocando o número do item +1, só fazer dessa forma que acabei de mostra, já incrementa o número.

E como fazemos isso no Class Operator?

...
class operator Positive(Value : TProduto) : TProduto;
...
class operator TProduto.Positive(Value : TProduto) : TProduto;
begin
    Result.Valor := Value.Valor + 1;
end;

Observe que dentro desse método positive, quando o objeto positive recebe um TProduto, ou seja, um positivo de TProduto, o retorno irá setar o parametro value.valor +1, incrementando  o valor passado e irá resultar no incremento do mesmo.

Viu como é bem simples esse incremento?

Isso tudo acontece implicitamente, onde o Class Operator sobrecarrega para nós.

A mesma coisa acontece com o decremento.

...
class operator Negative(Value : TProduto) : TProduto;
...
class operator TProduto.Negative(Value : TProduto) : TProduto;
begin
    Result.Valor := Value.Valor - 1;
end;
...
procedure TForm1.Button4Click(Sender : TObject);
var
    Prod : TProduto;
begin
    Prod := 10;
    Prod := -Prod;

    ShowMessage(IntToStr(Prod.Valor));
end;

Assim como foi feito no incremento, fazemos com o decremento.

Muito legal né pessoal?

Outro recurso que podemos sobrecarregar  é o Inc.

procedure TForm1.Button5Click(Sender: TObject);
var
    Prod : TProduto;
begin
    Prod := 10;
    Inc(Prod);
    ShowMessage(IntToStr(Prod.Valor));
end;

Você deve estar bem familiarizado com o Inc, não é verdade?

É muito utilizado esse operador, e com o Class Operator podemos também sobrecarrega-lo.

E se eu quiser dar um Inc somente na minha classe?

E lá dentro tratar qual o valor que irá ser incrementado?

Basta ir em nossa classe e implementar o Class Operator de Inc.

...
class operator Inc(Value : TProduto) : TProduto;
...
class operator TProduto.Inc(Value : TProduto) : TProduto;
begin
    Result.Valor := Value.Valor + 10;
end;

Observe que criamos o operador de incremento, onde todas as vezes que alguém incrementar com o TProduto, irá incrementar mais 10.

Eu to dizendo que o meu Inc não é mais 1, e sim mais 10.

Mas observe que a passagem de parâmetro do método, não estou passando um valor inteiro, e sim a classe TProduto.

Observe que não estou passando Inc no objeto inteiro e sim na classe.

procedure TForm1.Button5Click(Sender: TObject);
var
    Prod : TProduto;
begin
    Prod := 10;
    Inc(Prod);
    ShowMessage(IntToStr(Prod.Valor));
end;

Estou passando um valor para a classe, e logo em seguida estou incrementando o objeto prod, implicitamente ele vai incrementar no atributo valor.

Como eu disse que era 10, executei o Inc que está sobrecarregado com mais 10, então ele me retornou 20.

E se eu quiser um decremento de 2?

É só tratar dentro do método Negative, que ele irá decrementar o valor que você definir.

class operator TProduto.Negative(Value : TProduto) : TProduto;
begin
    Result.Valor := Value.Valor - 2;
end;

Agora todas as vezes que alguém fizer um decremento o negative ele vai decrementar 2.

E agora o último que iremos ver aqui é o operador Equal.

Vamos verificar primeiro antes sem o Equal para ver o comportamento do compilador sem ele.

procedure TForm1.Button6Click(sender: TObject);
var
    ProdA, ProdB : TProduto;
begin
    ProdA.Nome := 'Arroz';
    ProdA.Valor := 10;
    ProdB.Nome := 'Feijão';
    ProdB.Valor := 20;
    
    if ProdA = ProdB then
        ShowMessage('Sucesso')
    else
        ShowMessage('Erro');
end;

Observe que criei dois produtos, ProdA e ProdB, no ProdA eu coloquei Nome Arroz, e Valor 10, e no ProdB, Nome Feijão, e Valor coloquei 20.

E ai eu to fazendo uma estrutura condicional, onde eu verifico se ProdA é igual a ProdB.

Primeira coisa que acontece quando compilamos isso no Delphi, é retornado um erro.

Observe que o erro está informando que o operador não é aplicado ao tipo de objeto.

Ele está informando que não tem capacidade de comparar se o ProdA é igual ao ProdB, ele não é reconhecido, desta forma eu não tenho como comparar esses dois objetos, não consigo ver se esses dois objetos se um é igual ao outro.

Com a utilização do Class Operator é possível fazermos isso.

...
class operator Equal(A, B: TProduto) : Boolean;
...
class operator TProduto.Equal(A, B: TProduto) : Boolean;
begin
    Result := (A.Valor = B.Valor) and (A.Nome = B.Nome);
end;

Nós possuímos o atributo Equal do Class Operator, que é o atributo de igual.

Todas as vezes que alguém chamar o operador igual, e se minha classe possuir um Class Operator sobrecarregando esse operador, ele irá chamar esse método que criamos na classe de produto, onde é retornado verdadeiro ou falso o que foi tratado dentro desse método.

Se observar a documentação da Embarcadero você irá compreender como é trabalhado esse operador.

Do mesmo jeito você consegue fazer para diversos operadores dentro do Delphi.

Simplesmente dentro do método de Equal eu verifico se os valores completos são iguais, os Nomes e Valores, tanto para A quanto para B.

Agora a minha classe, tem a capacidade de dizer que dois atributos records do tipo TProduto são iguais ou não.

O que acabamos de fazer pode ser feito para qualquer classe sua, basta você criar o método de comparação.

No código que implementamos do botão de Equal, ele faz a verificação, vamos ver se irá mostrar a mensagem da verificação.

Observe que me foi retornado a mensagem de erro, isso ocorreu porque os valores de Nome são diferentes, onde ProdA = Arroz, e ProdB é Feijão.

Ele conseguiu comprar isso para nós e saber que ele são diferentes, agora vou igualar os valores e vamos ver o resultado.

Agora ele comparou certinho.

Criamos então uma funcionalidade a mais que não existia.

Muito legal não é pessoal?

Agora você conhece o que são os Class Operator, que é um recurso da linguagem, e você pode explorar a documentação do Delphi.

Existem vários operadores, e com todos esses operadores você pode trabalhar e sobrecarregar eles para suas classes não primitivas do Delphi.

Devemos nos atentar a um detalhe, o Class Operator não funciona na classe em si, quando for rodar para Windows, essa funcionalidade do Class Operator só foi adicionada para compiladores ARM, ou seja, para os compiladores Mobile, dessa forma você pode colocar os operadores nas classes.

Mas podemos contornar isso utilizando classes records, como mostrado aqui dentro do blog, e claro, dentro do CLUBE DE PROGRAMADORES EM DELPHI.

Esse é um conteúdo muito legal, e assim como este temos diversos outros dentro do CLUBE DE PROGRAMADORES EM DELPHI, onde você terá acesso a uma verdadeira Netflix do programador.

E caso você tenha interesse de conhecer mais sobre generics acessa o nosso portal do CLUBE DE PROGRAMADORES EM DELPHI, onde você não só terá conteúdos relacionados aos generics, mas uma quantidade enorme de conteúdos que poderá lhe ajudar muito no seu dia a dia, é uma verdadeira NETFLIX para os programadores Delphi.

CLIQUE AQUI E SAIBA MAIS SOBRE O CLUBE DOS PROGRAMADORES DELPHI


Faça sua busca

CATEGORIAS

POSTS RECENTES

E caso você tem interesse de conhecer mais sobre [Recursos do Delphi] Class Operator, acesse o nosso portal do CLUBE DE PROGRAMADORES EM DELPHI
Você não terá só conteúdos relacionados ao [Recursos do Delphi] Class Operator, mas uma quantidade enorme de conteúdos que poderá lhe ajudar muito no seu dia a dia, é uma verdadeira NETFLIX para os programadores Delphi.
Gostou?
Compartilhe:

Embarque no foguete com milhares de devs para aprender desenvolvimento, evoluir de forma contínua e se manter relevante no mercado.

Dúvidas
Cadastre-se em nossa lista

Para ter acesso em primeira mão, a tudo que acontece na Academia do Código, basta se cadastrar em nossa lista

Grupo Thulio Bittencourt | Academia do Código

#FaçaPartedaHistória

Copyright © 2024 – Todos os direitos reservados