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:

  • Todos os tipos de dados numéricos.(Byte - Short - Int - Long - Single - Double - Decimal)
  • Boolean, Char, e Date ,
  • Todas as estruturas(struct) , mesmo se seus membros são tipos de referência.
  • A enumerações (Enum) desde que seus tipos correspondentes sejam sempre : Byte, Short, Integer, ou Long

Os tipos por referência incluem:

  • String , Todos os vetores(arrays) , mesmo se seus elementos são tipos de valor, Tipos Classes , como um Form , Object , Class , Interface , Delegate ,

Observação : O tipo String possui as seguintes características especiais:

  • Não precisa ser iniciado com o operador New
  • Uma string contendo um Null é uma string vazia
  • Não podemos criar uma classe derivada de String
  • Strings contém caracteres unicode e podem ter até 1 Giga de comprimento

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
i
nt? 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
int j = 0

// a variável j receberá valor 42 pois m é nulo
j = m ?? 42;

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:


José Carlos Macoratti