.NET - Usando tipos de dados corretamente
Muitas pessoas que decidem aprender uma linguagem de programação como VB .NET ou C# geralmente não atentam para a importância de compreender os tipos de dados suportados e a forma correta de usá-los. No caso do VB .NET a atenção deveria ser redobrada visto que muitos estão migrando do VB5 ou VB6 para o VB .NET e houve alterações importantes que se não forem consideradas podem dar muita dor de cabeça no futuro.
Na tabela abaixo temos os tipos de dados do VB .NET:
Tipo de dados | tipo .NET Runtime | tamanho | intervalo |
Boolean |
System.Boolean |
4 bytes | True ou False |
Byte |
System.Byte |
1 byte | 0 até 255 (sem sinal) |
Char |
System.Char |
2 bytes | 0 até 65535 (sem sinal) |
Date |
System.DateTime |
8 bytes | Janeiro 1, 1 CE até Dezembro 31, 9999 |
Decimal |
System.Decimal |
12 bytes | +/-79,228,162,514,264,337,593,543,950,335 sem ponto decimal ; +/-7.9228162514264337593543950335 com 28 casas a direita do decimal; o menor valor diferente de zero é algo em torno de 0.0000000000000000000000000001 |
Double |
System.Double |
8 bytes | -1.79769313486231E308 até -4.94065645841247E-324 para valores negativos; 4.94065645841247E-324 até 1.79769313486232E308 para valores positivos; |
Integer
|
System.Int32 |
4 bytes | -2,147,483,648 até 2,147,483,647 |
Long
|
System.Int64 |
8 bytes | -9,223,372,036,854,775,808 até 9,223,372,036,854,775,807 |
Object
|
System.Object
(class) |
4 bytes | Qualquer tipo pode ser armazenado. |
Short
|
System.Int16 |
2 bytes | -32,768 até 32,767. |
Single
|
System.Single
|
4 bytes | -3.402823E38 até -1.401298E-45 para valores negativos; 1.401298E-45 até 3.402823E38 para valores positivos; |
String
|
System.String (class)
|
10 bytes + (2 * tamanho da string) | Até aproximadamente dois bilhões de caracteres Unicode. |
User-Defined Type (estrutura) | (herda de
System.ValueType )
|
Soma dos tamanhos dos seus membros | Cada membro da estrutura possui um intervalo determinado pelo seu tipo de dados e é independente dos intervalos dos demais números; |
Eu vou simular um cenário para mostrar que um pequeno detalhe pode afetar , dependendo do ambiente, o comportamento esperado por um programa.
Vamos supor que um programador trabalhando para uma instituição financeira é encarregado de criar um módulo para tratar o saldo de operações em um determinado tipo de conta onde o cliente que possui um saldo positivo poderá programar amortizações ou retiradas programadas até zerar o saldo.
Assim, se a conta possui um saldo de 100.00 ele poderá efetuar amortizações/retiradas em parcelas mensais; por exemplo 10 parcelas de R$ 10,00 até o saldo final ser igual a zero. Se o saldo for negativo o programa não poderá permitir retirada/amortização. Com isso definido vamos ao código que o programador usou:
Usando o Visual Basic 2008 Express Edition vamos criar um projeto chamado DoubleNet e no formulário form1.vb vamos definir o seguinte leiaute:
Controles usados
no formulário:
É definido o saldo Atual e o valor da parcela da retirada. Para simular as retiradas é usado um loop que será executado até que o saldo for maior que zero onde do valor do saldo atual será subtraído o valor da parcela; a cada interação incrementamos um contador para saber o número de retiradas que foi efetuada. Abaixo temos o trecho de código usado : While seuSaldo > 0 seuSaldo -= parcela End While |
Como estamos usando um ListBox para exibir o resultado o código final associado ao evento Click do botão - Simular 10 Retiradas - é o seguinte:
Private Sub btnSimular_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSimular.Click'limpamos o controle ListBox e indicamos que estamos usando um Double lstbResultado.Items.Clear() lstbResultado.Items.Add(" ---- Usando Double ----")
'obtemos o valor do saldo da parcela e definimos o valor da variável para contar as retiradas como sendo igual a zero Dim seuSaldo As Double = txtSaldo.Text Dim totalRetiradas As Integer = 0 Dim parcela As Double = txtParcela.Text
'executamo o laço While enquando o saldo for maior que zero While seuSaldo > 0 seuSaldo -= parcela
totalRetiradas += 1 'exibimos o total de retiradas e o saldo no ListBox lstbResultado.Items.Add("Retirou " & totalRetiradas & " parcela") lstbResultado.Items.Add("Saldo Atual: " & seuSaldo)
End
While 'Após encerrar o laço exibimos o total de retiradas lstbResultado.Items.Add("-----------------") lstbResultado.Items.Add("Vocˆ retirou " & totalRetiradas & " parcelas!")
End Sub |
Vamos agora verificar se a lógica esta correta testando executando o programa e fazendo alguns testes:
a-) Teste com saldo inicial igual a R$ 100,00 e parcela igual a 10,00:
O resultado confere e parece que tudo esta correto.
Vamos agora testar com um saldo atual igual a 150,50 e parcela de 15,05. Estaremos esperando 10 retiradas de R$ 15,05 , correto ???
Executando o projeto iremos obter:
O resultado foi uma surpresa , não é mesmo ???
Veja que na décima retirada o saldo é um valor muito pequeno mas diferente de zero e assim foi permitido uma nova retirada. Uma catástrofe...
Mas qual o erro estamos cometendo ? Erro de lógica ? Erro de cálculo ?
Nada disso o erro esta nas seguintes linhas de código :
Dim seuSaldo
As Double = txtSaldo.Text
Dim parcela As
Double = txtParcela.Text
Mas Onde ????
O programador definiu as variável como sendo do tipo Double que suporta uma precisão numérica muito grande assim na décima parcela retirada o saldo é um número muito pequeno ( 3,552713678805 elevado a -15) que devido a precisão do tipo de dados usado é levado em conta e computado.
Dessa forma o saldo não é menor que zero permitindo assim uma nova retirada.
Desta forma um pequeno detalhe poderia dar uma grande dor de cabeça.
Um Double possui um tamanho de 8 bytes e atua no intervalo de : -1.79769313486231E308 até -4.94065645841247E-324
Como resolver o problema não permitindo que a diferença seja computada ?
Conselho : No VB .NET para efetuar cálculos com valores monetários devemos usar o tipo Decimal.
No VB6 havia o tipo Currency que era usado para cálculos financeiros mas no VB .NET ele não existe e foi substituído pelo tipo Decimal.
Se você pretende continuar usando o VB6 a sugestão é não usar o tipos de dados Currency , use o tipo de dados Decimal no VB6 e no VB .NET use o tipo de dados Decimal ou Long
Além desta alteração temos abaixo uma tabela comparando o tipo de dados usado no VB6 e o atualmente usado no VB .NET:
VB6 |
VB.NET |
Comentários |
Integer |
Short |
16 bits |
Long |
Integer |
32 bits |
N/A |
Long |
64 bits |
Variant |
N/D |
Use o novo tipo de dados Object |
Currency |
N/D |
Use o Decimal no VB6 ou Decimal ou Long no VB .NET. |
N/D |
Decimal |
Disponível no VB6(converter de Variant para Decimal com a função cDec.) Nativo no VB.NET |
String |
String |
O VB.NET não suporta strings de tamanho fixo. |
Para usar o tipo de dados Decimal no VB6 você pode declarar a variável como Variant e usar a função cDec para convertê-la para Decimal.
Conselho : A regra de ouro é sempre declarar as variáveis entes de usá-las. (Nunca use Option Explicit Off)
A opção Option Strict é nova no VB.NET ; usando a opção Option Strict evitamos os erros em tempo de execução que se originam de conversões automáticas de variáveis.
Assim , na conversão de uma variável do tipo Int32 para o tipo Int16 ocorre um estreitamento que pode ou não dar certo pois podem existir valores que ao serem convertidos para int16 percam a precisão. Se você trabalhar com a opção Option Strict desativada , o VB vai deixar você tentar , se der certo muito bem se não der ....
Vamos a um exemplo :
O código abaixo trabalha com a opção Option Strict desativada :
Option Strict Off Private Sub Calculo(NumeroMaior as Int32 , NumeroMenor as Int16) Dim troca as int32 troca = NumeroMaior NumeroMenor = troca End Sub |
O VB não vai reclamar e não indicará erro neste código seu projeto vai rodar bem até que você tente fazer a conversão de um número maior que 32.767 , ai sim vai ocorrer um erro na sua aplicação...
Se ativarmos a opção Option Strict o código fica assim :
Option
Strict On
Private Sub Calculo(NumeroMaior as Int32 , NumeroMenor as Int16) Dim troca as int32 troca = NumeroMaior NumeroMenor = troca if NumeroMaior > NumeroMenor.Value then MessageBox " Atenção ! Este número não suporta a conversão " else NumeroMenor = Ctype(troca, Int16) endif End Sub End Sub |
Com a opção Option Strict ativada , a coisa muda de figura. Durante a fase de projeto o VB.NET irá sublinhar o código e será gerado um erro em tempo de compilação. O VB.NET não permite a você usar o código sem fazer a modificação explícita ( manual ) . Você é avisado em tempo e pode ajustar o seu código.
Conselho : Use Option Explicit e Option Strict sempre ativas !
Tanto o VB.NET como C# são linguagens fortemente tipadas ( como Java), isto significa que o compilador verifica por compatibilidades de tipos em tempo de execução em quase todos os casos , impedindo atribuições incompatíveis , proibindo atribuições questionáveis e fornecendo casts quando a compatibilidade de um tipo puder ser determinada apenas em tempo de execução.
Algumas conversões ocorrem automaticamente, sem você ter que se preocupar. Por padrão no VB.NET o casting é automático quando você atribui objetos a variáveis. Os objetos são então convertidos à força para o tipo da variável. Este comportamento pode ser influenciado pelas declarações:
- Option
Strict On - (padrão) -
o casting é restrito é não é automático
- Option Strict Off -
permite conversões implícitas no seu código
Para realizar um casting explícito (conversão forçada) podemos usar o operador Ctype() ou DirectCast()
Dim Q As Object = 2.37 ' Requer Option Strict Off. Dim I As Integer = CType(Q, Integer) ' Funciona Dim J As Integer =DirectCast(
Q, Integer)
' Falha
Ambas as palavras chaves tomam uma expressão a ser convertida como primeiro argumento e o tipo a converter como segundo argumento.
Se não houver uma conversão definida entre o tipo de dados da expressão e o tipo especificado a ser convertido tanto Ctype como DirectCast não irão funcionar. Tanto Ctype como DirectCast lançam a exceção InvalidCastException.
A função CType opera em dois argumentos. A primeira é a expressão a ser convertido, e o segundo é a classe tipo ou objeto de dados de destino. Observe que o primeiro argumento deve ser uma expressão, não um tipo. CType é uma função in-line, que significa que o código compilado faz a conversão, com freqüência sem gerar um chamada de função. Isso melhora o desempenho.
Vejamos a seguir as principais funções de conversão no VB .NET:
Função | Descrição | Exemplo |
Cbool | Converte para um Booleano ( False ou True). False ou 0 será definido como False. Retorna um Boolean |
Dim A, B, C As Integer Dim Check As Boolean A = 5 B = 5 Check = |
Cbyte | Converte para um Byte . Qualquer valor maior que 255 ou valor fracionário será perdido. Retorna um Byte. |
Dim MyDouble As Double Dim MyByte As Byte MyDouble = 125.5678 MyByte = |
CChar | Converte para um Caracter . Qualquer valor maior que 65,535 será perdido e , se você tentar converter uma string somente o primeiro caractere será convertido. |
Dim MyString As String Dim MyChar As Char MyString = "BCD" 'converte só o primeiro caractere MyChar = |
CDate | Converte para um Date. Aceita qualquer representação de data e tempo. | Dim
MyDateString, MyTimeString As String Dim MyDate, MyTime As Date MyDateString = "February 12, 1969" MyTimeString = "4:35:47 PM" ' ... MyDate =
CDate( MyDateString)
' Converte p/ o tipo Date.MyTime = CDate( MyTimeString)
' Converte p/ o tipo Date. |
CDbl | Converte para um Double. | Dim MyDec As
Decimal Dim MyDouble As Double MyDec = 234.456784D . MyDouble =
CDbl( MyDec * 8.2D * 0.01D)
' Converte para Double |
CDec | Converte para um Decimal. | Dim MyDouble
As Double Dim MyDecimal As Decimal MyDouble = 10000000.0587 MyDecimal =
CDec( MyDouble)
' Converte para Decimal. |
CInt | Converte para um inteiro. Valores de -2,147,483,648 até 2,147,483,647 . Frações são arredondadas. |
Dim MyDouble As Double Dim MyInt As Integer MyDouble = 2345.5678 MyInt = |
CLng | Converte para um Longo. Valores -9,223,372,036,854,775,808 até 9,223,372,036,854,775,807. Frações são arredondadas. | Dim MyDbl1,
MyDbl2 As Double Dim MyLong1, MyLong2 As Long MyDbl1 = 25427.45 MyDbl2 = 25427.55 MyLong1 =
CLng( MyDbl1)
' MyLong1 conterá 25427.MyLong2 = CLng( MyDbl2)
' MyLong2 conterá 25428. |
CSht | Converte para um Short. Valores de 32,768 a 32,767. Frações são arredondadas. | Dim MyByte
as Byte Dim MyShort as Short MyByte = 100 MyShort = CShort(MyByte) ' Converte para Short. |
CStr | converte para um String. Se for uma Data o retorno será no formato - Short Date. |
Dim MyDouble As Double Dim MyString As String MyDouble = 437.324 MyString = |
CSng | Converte para um Single . -3.402823E+38 até -1.401298E-45 // 1.401298E-45 até 3.402823E+38 |
Dim MyDouble1, MyDouble2 As Double Dim MySingle1, MySingle2 As Single MyDouble1 = 75.3421105 MyDouble2 = 75.3421567 MySingle1 = |
CObj | Converte para um objeto. |
Dim MyDouble As Double Dim MyObject As Object MyDouble = 2.7182818284 MyObject = |
Ctype | Converte para qualquer tipo de dados. Sintaxe: Variavel = Ctype( TipoVelho , NovoTipo) | Dim MyNumber
As Long Dim MyNewType As Single MyNumber = 1000 MyNewType = CType(MyNumber,Single) ' MyNewType é igual a p/1000.0 |
Nota:
Se a expressão submetida a função estiver fora do intervalo do tipo de dados para o qual você quer converter ocorrerá um erro
Usamos estas funções para forçar que o resultado de uma operação seja de um tipo particular diferente do resultado padrão. Assim usamos CDec para forçar para decimal em casos no qual a precisão simples, dupla ou um valor inteiro normalmente iria ocorrer.
Se o valor fracionário submetido for exatamente 0,5 , CInt e CLng irão arredondar para o número par mais próximo. Assim 0,5 será arredondado para 0 e 1,5 será arredondado para 2.
CDate reconhece o formato de datas de acordo com a configuração local do sistema. Você deve informar o dia , mês e ano na ordem correta de acordo com a configuração local.
Agora o último conselho: "Quem avisa amigo é..."
Até o próximo artigo...
Referências:
José Carlos Macoratti