VB .NET - Nothing , Null (DBNull.Value) e Nullabe Types
Você sabe me dizer a diferença entre Nothing e Null (DBNull.Value) no VB .NET ?
Antes de entrar na discussão vejamos as seguintes definições:
Tipos de dados por Valor e por
referência Um tipo de dado e considerado tipo por valor se ele trata o dado dentro da sua própria alocação de memória. Já um tipo de dado por referência contêm um ponteiro para outra alocação de memória que gerencia o dado. Os tipos por valor incluem:
Os tipos por referência incluem:
Observação : O tipo String possui as seguintes características especiais:
Nota : Você pode atribuir um tipo de valor ou tipo de referência para uma variável do tipo objeto . Uma variável do tipo objeto sempre possui um ponteiro para o dado nunca o próprio dado. Porém quando você atribui um tipo de valor para um variável objeto ela se comporta como se possuísse o próprio dado. Como saber então se uma variável objeto esta atuando por referência ou por valor ? R : Para saber se uma variável objeto esta atuando como um tipo por referência ou por valor você pode usar o método IsReference da classe Information do namespace Microsoft.VisualBasic. Se Microsoft.VisualBasic.Information.IsReference retornar True o conteúdo da variável objeto representa um valor por referência. |
Agora vamos criar um projeto do tipo Console no Visual Basic 2008 Express Edition com o nome NullabeTypes e informa o seguinte código no evento Load do formulário form1.vb:
Dim M As Object Dim C As Integer Debug.Print("1. M não esta definido.") Debug.Print("M é Nothing: " & (M Is Nothing)) Debug.Print("M é DBNull.Value: " & (M Is DBNull.Value)) Debug.Print("----------") M = New Object() Debug.Print("2. M esta definido para um novo objeto.") Debug.Print("M é Nothing: " & (M Is Nothing)) Debug.Print("M é DBNull.Value: " & (M Is DBNull.Value)) Debug.Print("----------") M = DBNull.Value Debug.Print("3. M esta apontando agora para a classe especial DBNull.") Debug.Print("M Is Nothing: " & (M Is Nothing)) Debug.Print("M Is DBNull.Value: " & (M Is DBNull.Value)) Debug.Print("----------") C = Nothing Debug.Print("4. C não esta definido.") Debug.Print("C Is Nothing: " & (IsNothing(C))) Debug.Print("C Is DBNull.Value: " & (IsDBNull(C))) |
Resultado da execução do código ao lado: 1. M não esta definido. M é Nothing: True M é DBNull.Value: False ---------- 2. M esta definido para um novo objeto. M é Nothing: False M é DBNull.Value: False ---------- 3. M esta apontando agora para a classe especial DBNull. M Is Nothing: False M Is DBNull.Value: True ---------- 4. C não esta definido. C Is Nothing: False C Is DBNull.Value: False |
Vamos tentar entender o que esta acontecendo ????
1-
Este resultado ocorre quando declaramos uma variável M
do tipo Object e não fazemos nenhuma atribuição a ela; Ao inspecionar qual o seu valor logo após a sua declaração veremos que M é Nothing pois não atribuímos nenhum valor a ela; Como a variável M não possui um valor ela não esta apontando para o objeto DBNull.Value, logo IsDBnull() será false; |
2- Aqui criamos um
novo objeto (new Object()) e apontamos M
para ele. Agora M não é Nothing, pois
esta apontando para o novo objeto que criamos; Dessa forma a variável M não esta apontando para o objeto DBNull.Value e portanto IsDBNull() retorna false; |
3-
Agora definimos M apontando para DBNull.Value
e agora IsNothing retorna falso; Como M é um ponteiro e DBNull.Value é um objeto , M esta apontando para este objeto, por isso, ele não é Nothing; Neste caso IsDBNull() retorna True pois M esta apontando para DBNull.Value. |
4-
Finalmente estamos usando a variável C definida como um
Integer, ou seja um variável de valor; Definimos a variável como Nothing (C=Nothing) mas ao verificarmos a variável temos que ela não é Nothing; Isso ocorre pois tão logo fazemos a atribuição como Nothing , ela retorna ao seu valor padrão que é zero pois é um tipo por valor (value type); |
Agora se você costuma trabalhar com banco de dados já passou pelo problema de alinhar objetos de negócio com esquemas de banco de dados.
Uma das dificuldades enfrentada é que em muitos casos um campo de uma tabela do banco de dados, mesmo sendo um inteiro, pode não possuir um valor definido , neste caso temos o Null ou Nothing.
Na plataforma .NET os tipos por valor como Integers sempre possuem um valor, e, neste caso quando vamos retornar uma informação do banco de dados é preciso incluir uma lógica adicional para indicar se um dado campo possui ou não um valor atribuído caso contrário teremos um exceção.
Obs: Uma das soluções é ajustar o esquema do banco de dados para não permitir valores nulos, outra seria incluir um valor boleano a cada campo que pode não possuir um valor inicial.
A partir da versão 2.0 a plataforma .NET trouxe uma solução para este problema que são os Nullabe Types.
Esta solução consiste em permitir que os tipos por valor possam ser estendidos para ter ou o seu valor normal ou um valor null.
Essa extensão é chamada de Nullabe Type.
Os
Nullabe Type são instâncias da
estrutura System.Nullabe e podem
representar o intervalo normal de valores para o seu
valor de tipo correspondente mais o valor adicional null. Por exemplo: Nullable(Of Integer) pode ser atribuído a qualquer valor inteiro e também pode ser atribuído ao valor null.(nulo). |
Um Nullable type é construído a partir de um estrutura Nullable genérica e desta forma podemos submeter valores null para o campo da tabela do banco de dados sem problemas, como submeter um valor DateTime null para um campo do tipo data do banco de dados.
Como podemos usar este recurso ?
É muito simples basta declarar a variável como tendo a extensão Nullable:
VB .NET |
C# |
Dim dataNascimento
As Nullable(Of DateTime) Dim idade As Nullable(Of Integer) Dim ativo As Nullable(Of Boolean) |
DataTime?
dataNascimento = null; int? idade = null; bool? ativo = null; |
Com essas declarações as variáveis poderão receber o valor normal definido para o seu tipo mais o valor null (nulo);
E como verificar se uma variável definida como um nullabe type contém dados ?
Neste caso basta usar a propriedade HasValue anexada da seguinte forma:
VB .NET |
C# |
If
dataNascimento.HasValue Then '...fazer algo End If |
if
(dataNascimento.HasValue) { //...fazer algo } |
A propriedade HasValue retorna true se a variável contém um valor ou false se ela contém um valor null.
Para extrair o valor norma a partir de um Nullable Type usamos a propriedade Value somente se HasValue retornar True.
VB .NET |
C# |
'idade
pode ser um Int32 ou Nulo Dim idade As System.Nullable(Of Integer) = Nothing If idade.HasValue Then MessageBox.Show("idade contém um valor inteiro válido") Else MessageBox.Show("idade contém um valor nulo") End If |
//idade
pode ser um Int32 ou Nulo int? idade = null; if (idade.HasValue) MessageBox.Show("idade contém um valor inteiro válido"); else MessageBox.Show("idade contém um valor nulo"); |
A propriedade Value retorna um valor se a variável possuir um valor atribuído, caso contrário lança a exceção System.InvalidOperationException();
VB .NET |
C# |
'idade
pode ser um Int32 ou Nulo Dim idade As System.Nullable(Of Integer) = Nothing 'temp é um valor Int32 Dim temp As Integer = 0 Try temp = idade.Value Catch generatedExceptionName As InvalidOperationException MessageBox.Show("Operação não permitida!") End Try |
//idade
pode ser um Int32 ou Nulo int? idade = null; //temp é um valor Int32 int temp = 0; try{ temp = idade.Value; } catch (InvalidOperationException ){ MessageBox.Show("Operação não permitida!"); } |
Você pode usar o método System.Nullable.GetValueOrDefault() para retornar o valor atribuído ou o valor default para o tipo se o valor for null.
VB .NET |
C# |
'
idade pode ser um Int32 ou Nulo Dim idade As System.Nullable(Of Integer) = Nothing ' temp vai receber o valor zero , valor padrão para o tipo Int32 Dim temp As Integer = x.GetValueOrDefault() |
// idade
pode ser um Int32 ou Nulo int? idade = null; // temp vai receber o valor zero , valor padrão para o tipo Int32 int temp = x.GetValueOrDefault(); |
Ex: Dim data as DateTime = dataNascimento.GetValueOrDefault();
Operador ?? (C#)
Em C# temos o operador ?? através do qual é possível determinar o valor que terá a variável quando contiver o valor nulo. Exemplo:
//variável com valor null int? m = null; //variável do tipo int32 // a
variável j receberá valor 42 pois m é nulo |
Concluindo temos que os tipos nulos
(Nullabe Types) têm as seguintes características:
- Os Nullabe Types (Tipos Nulos) representam variáveis de tipo
de valor aos quais é possível atribuir o valor de null.
Obs:
Você não pode criar um tipo anulável baseado em um tipo de
referência.(Tipos por referência já suportam o valor null.)
- A sintaxe T? é abreviada de Nullable(Of
T), onde o T é um tipo de valor. As duas formas são
intercambiáveis.
- Você pode Atribuir um valor para um tipo anulável como faria
para um tipo de valor comum, por exemplo, int? x = 10; ou double?
d = 4.108;
- Use o operador de ?? para atribuir um valor padrão que será
aplicado quando um tipo anulável cujo valor atual é nulo é
atribuído a um tipo não-nulo.
Ex: int? x =
null; int y = x ?? -1;
- Os Tipos anuláveis (ou tipos nulos) aninhados não são permitidos. A seguinte
linha não irá Compilar: Nullable<Nullable<int>>
n;
Eu espero ter contribuído para
ajudar a dissipar a nuvem que paira sob como tratar valores
Nulos, principalmente em operações com banco de dados.
Eu sei é apenas VB .NET, mas eu gosto...
Referências: