Tipos (Structs) "Nuláveis" em C# (exemplo: DateTime?) (Nullable Types)
Uma vez, quando fiquei alocado para arrumar um problema de desempenho em um programa em C# (um Windows Service que faz acesso a um banco Oracle e a um webservice) encontrei algo que me deixou curioso, uma declaração da seguinte forma:
1: public DateTime? MinhaData;
Fiquei curioso pelo fato do ponto de interrogação que sucede o tipo DateTime (DateTime?). Nunca tinha visto este tipo de declaração.
Pesquisando o que significava, pensando que é algum tipo de modificador, descobri que se sucedermos um tipo de dados pelo ponto de interrogação, significa que poderemos atribuir o estado null para ele.
Como assim?
Se, por exemplo, atribuirmos null para uma variável do tipo DateTime ou int (só para citar mais de um), ocorrerá um erro de compilação (ou execução...) com a seguinte mensagem:
Cannot convert null to 'System.DateTime' because it is a value type
Os tipos DateTime e int dos nossos exemplos são implementados com struct, e de acordo com a mensagem acima, ele é um tipo por valor, ou seja, um tipo primitivo (armazena somente valores, como datas e números).
Tipos primitivos, embora tenhamos métodos sobre eles, não são objetos, que são tipos por referência (se você atribui um objeto A para o B, qualquer modificação no B refletirá em A, pois B faz uma referência a A - apontam para o mesmo endereço de memória -, e não é uma cópia de A - em outro ponto da memória.). Por causa dos métodos que temos é que eles são implementados sendo struct (que pode ter métodos, como uma classe). Só para ter uma noção da coisa, o tipo string do .NET é implementado como uma classe, ou seja, é um objeto, e portanto, é um tipo por referência e o estado nulo pode ser atribuído.
O ponto de interrogação "transforma" este tipo por valor em um tipo por referência, ou seja, significa que poderemos atribuir o estado null para variáveis dele.
Uma declaração do tipo abaixo não irá gerar erros de compilação ou execução:
1: public DateTime? MinhaData = null;
Isto é útil principalmente quando atribuimos nestas variáveis valores oriundos de um banco de dados, que podem estar nulos ou não, e não queremos trabalhar a toda hora com variáveis string, como comumente é feito.
Como nem tudo na vida são flores, isso tem algumas desvantagens. Uma delas é que não temos mais as propriedades e métodos do struct DateTime (como AddDays, Month, Year, entre outros) diretamente. Veja a imagem abaixo, demonstrando o code-insight em uma variável DateTime? :
Duas propriedades importantes deste novo tipo, como em qualquer outro tipo "nulável", são as propriedades Value e a HasValue. A primeira é a que conterá o tipo original em questão: Se for DateTime, ela será do tipo DateTime, se for int, será int.
Dentro da propriedade Value é que estarão todos os métodos do tipo original:
A propriedade HasValue, do tipo bool, indica se a variável não é nula. Isto é importantíssimo no momento de fazermos ou uma atribuição para um tipo original, quanto para fazer comparações, no nosso caso, demonstrarei como um if a mais no código exemplo abaixo:
1: DateTime x = DateTime.MinValue;
2: DateTime? y = null;
3:
4: if (x.Equals(y))
5: {
6: MessageBox.Show("alguma coisa");
7: }
8:
9: if (x < y)
10: {
11: MessageBox.Show("outra coisa");
12: }
No código acima, devemos tomar alguns cuidados: Null é um ESTADO, e não um VALOR. Portanto, comparar as duas variáveis é um tiro no pé. No segundo if, quem é menor e quem é maior?
Esta pergunta não tem resposta! Null não é nem menor, nem igual, nem maior a um valor. A única comparação que será verdadeira será a da diferença. Não vai gerar exception no código acima, mas ele está ilógico. Já o exemplo abaixo vai dar um erro de execução, mas vai ser compilado:
1: DateTime x = DateTime.MinValue;
2: DateTime? y = null;
3:
4: if (x.Equals(y))
5: {
6: MessageBox.Show("alguma coisa");
7: }
8:
9: if (x > y.Value)
10: {
11: MessageBox.Show("outra coisa");
12: }
O erro será a seguinte mensagem:
Nullable object must have a value.
Isso significa, que antes de fazer o If, temos que verificar se existe algum valor na variável y. O correto a fazer é:
1: if (y.HasValue)
2: {
3: if (x > y.Value)
4: {
5: MessageBox.Show("outra coisa");
6: }
7: }
Tivemos um trabalho a mais neste código.
Como vimos, este é um recurso que embora muito útil, devemos utilizá-lo com bastante cuidado para não termos surpresas lá na frente ;-)
Para saber mais: http://msdn.microsoft.com/en-us/library/1t3y8s4s(VS.80).aspx
Um abraço!
2 comentários:
Obrigado!! Foi muito útil! :)
bom, bem explicado
Postar um comentário