Olá a todos.
O .Net 2.0 traz um recurso deveras interessante, embora, à primeira vista, não o pareça.
Primeiro: Revendo Value-Types e Reference-Types
Antes de falar de Nullable types, é necessário relembrar a diferença entre value-types e reference-types. Se você já conhece bem esta diferença, você vai achar mais interessante ir para o próximo tópico.
Value-types e Reference-Types são os primeiros grandes grupos de tipos do .Net, dos quais todos os outros tipos derivam. O primeiro grupo (value-types), compreende todos os tipos de dados numéricos (byte, sbyte, int, uint, long, ulong, char, bool, float, double e decimal) bem como Enumeradores (enum) e Structures (struct).
Value-types são alocados no Stack. Isto faz com que eles sejam facilmente removidos da memória uma vez que eles ficam fora de escopo. Quando você atribui o valor de uma variável de um tipo value-type para outra, uma cópia do seu valor é atribuído para a outra. No caso de duas variáveis do tipo int, seu valor é simplesmente copiado (chega a ser redundante repetir isto). Mas no caso de uma struct, a cópia realizada é uma cópia de membro por membro.
Começamos o primeiro exemplo, definindo um struct. O struct NomeCompleto guarda Nome e Sobrenome:
Este pequeno programa, irá copiar um NomeCompleto para outro e logo após fará alteração em um dos membros.
Ao fazer a cópia, observe que nomeCompleto2 possui o mesmo conteúdo de nomeCompleto1. Mas ao fazer a alteração no sobrenome de nomeCompleto2, você nota que nomeCompleto1 permanece com o estado original. Isto se dá por que, diferente das classes, que são reference types, as structs são value-types, e passam todos os seus valores quando são atribuídas a alguém.
Se eu simplesmente redeclarar a struct como class, o resultado altera de forma considerável. Isto por que class é um reference type, o que significa que, na atribuição, ele não recebe os valores contidos no objeto, mas sim uma referência ao endereço que o mesmo ocupa na memória. Sendo assim, tudo o que for aplicado a um, reflete no outro. (Na verdade, o que se aplica em um reflete somente nele mesmo, e isto faz com que as alterações sejam visíveis nas outras referências.) Observe o resultado do mesmo programa, apenas alterando a declaração do struct NomeCompleto para class:
Note que quando mudamos o sobrenome de nomeCompleto2, a alteração também se torna visível no nomeCompleto1.
No fim das contas, Value-Types são tipos que administram os próprios valores. Sempre se copiando ao serem manipulados. Reference-Types são tipos que administram referências a objetos alocados na memória. Agora que você entende esse tópico básico, você está pronto para conhecer o recurso tema deste artigo: Nullable Types.
Nullable Type Itself
Existe uma limitação que nunca foi realmente um problema (mas vale a pena conhecer sua solução). Variáveis de um tipo value type não aceitam valores nulos. Sendo assim, atribuir um valor null a uma variável do tipo int provocava um erro de compilação.
Nós aprendemos a lidar com esta limitação do framework, usando flags como int.MinValue para indicar valores inválidos ou algum outro tipo de tolerância.
Com o recurso de Nullable types disponível (sinceramente eu não consigo encontrar uma tradução saudável para o termo Nullable em português) conseguimos alguma trégua para lidar com isto de uma forma mais polida.
Suponhamos a situação da seguinte (e muito simples) tabela:
O primeiro registro possui um valor no campo Idade, mas o segundo, por alguma razão, foi deixado como Null.
O seguinte código vai provocar um erro quando o registro lido for o de Burns.
Isso por que o campo Idade possui um valor Null, que é inaceitável para uma variável do tipo byte, e isso provoca uma SqlNullValueException. A manobra que se fazia era setar a variável com o minValue específico do seu tipo e verificar se o campo é nulo antes de fazer a atribuição.
Nullable types são tipos criados especificamente para este tipo de situação: Atribuir valor Null a variáveis de algum tipo Value Type.
Somente value types podem se tornar nullable types e a forma de fazer isto é muito simples. Usando o operador "?" ao lado da indicação de tipo. Exemplo:
Desta forma, valores nulos poderão ser associados à estas variáveis.
Outras Utilidades
Como desenvolvedor, você precisa ter em mente de que as situações onde se aplicam recursos como Nullable Types são inesperadas. À primeira vista pode parecer um recurso inútil, mas no seu dia-a-dia vai compensar ter conhecimento deste recurso por ser aplicável a uma circunstância inesperada.
No meu caso, enums e structs.
Quase todos os enums de um determinado projeto do qual eu fazia parte, precisavam contemplar um elemento None pelo simples fato de não ser possível atribuir null à ele. O mesmo para structs que deveriam ser usados em determinados pontos.
Acreditem quando eu digo, mais vale a pena ter conhecimento de um recurso e nunca precisar usá-lo do que precisar sem nunca saber de sua existência.
O Operador "??"
Uma mão na roda para auxiliar ainda mais no uso de Nullable types, é o operador "??". Ele dá uma alternativa de valor para o caso do operando ser nulo. Por exemplo:
O operador "??" verifica se o operando velMax possui valor Nulo e, no caso de ser verdade, como forçamos na linha anterior, dá um valor alternativo (80).
A classe System.Nullable
O suporte a Nullable Types no .Net é permitido pela ocasião da Classe System.Nullable
(Que também foi criada por ocasião do uso de Generics). Todos os NullableTypes na verdade são wrappers para instancias de Nullable. Ou seja, "int?" é um wrapper parar Nullable, "float?" é um wrapper para Nullable, e assim por diante.
É possível confirmar isso abrindo o código IL gerado pelo compilador. Ou, menos complicado para os que não conhecem IL, executando um código como este:
E verificando o seguinte resultado:
Ambos tipos, em design-time, são o mesmo tipo em run-time, ou seja, mapeiam um mesmo tipo durante a compilação.
Conclusões
Este pequeno artigo mostrou que, apesar disso nunca ter sido realmente um problema sério (talvez por que todos nós nos acostumamos com a limitação) os value types, que não suportavam valor nulo, receberam tipos equivalentes que o suportam: Nullable Types.
Com Nullable Types algumas limitações dos value-types podem ser superadas. Ainda que você não veja utilidade alguma nisto, e resolva nunca fazer uso deste recurso, tenha em mente que ainda assim pode ser útil conhecê-lo, pois ele cai no exame para a certificação.
Por Daniel Moreira Yokoyama
Daniel Moreira Yokoyama ([email protected]) atua na Envision Tecnologia, no desenvolvimento de um sistema web de pesquisa por disponibilidade e reserva de assentos em vôos para uma grande consolidadora de agentes de viagem. É formando em Ciência da Computação e trabalha com desenvolvimento há mais de 7 anos, tendo contato didático com .Net desde a aparição do primeiro beta do VisualStudio.Net e trabalhando com ele há quase 3 anos.
Conheça nosso parceiro: