Pentru ca tipurile referinta pot indica prin null faptul ca nu prezinta nici o valoare, pentru tipurile valoare s-a introdus constructia nullable.
//atribuie valoarea null pentru un obiect
Agent a = null;
//eroare la compilare
int i = null;
Un tip nullable inseamna ca pe langa valorile implicite pe care le are, el va avea si valoarea null.
De exemplu, un tip de date bool poate avea valorile false sau true.
Un tip de date nullable bool va avea valorile false, true, null.
La fel, un nullable int va avea valorile intre -2.147.483.648 si 2.147.483.647 si null.
Declararea unui tip nullable se face in modul urmator:
int? id = 5;
//FALSE
Console.WriteLine(id == null);
bool? isAType = null;
//TRUE
Console.WriteLine(isAType == null);
Echivalentul:
Nullable<int> id = new Nullable<int>(5);
//FALSE
Console.WriteLine(id == null);
Nullable<bool> isAType = new Nullable<bool>();
//TRUE
Console.WriteLine(isAType == null);
Structura nullable face parte din namespace-ul System.
Proprietea HasValue va returna true daca variabila de tip nullable are o valoare. Cu ajutorul proprietatii Value se va putea prelua valoarea.
Proprietatea GetDefaultValue va returna valoarea default a tipului in cazul in care variabila are valoarea null.
int? b = null;
//0
Console.WriteLine(b.GetValueOrDefault());
Operatorul ??
Operatorul ?? defineste o valoare implicita care va fi returnata in cazul unui tip nullable pentru cazul null (daca se incearca o atribuire pentru o valoare a unui tip nullable cu o valoare a unui tip non-nullable, va genera eroare la compilare).
De exemplu, construim clasa Agent ce va avea o proprietate de tip nullable.
class Agent
{
public int Id { get; set; }
public int? Code { get; set; }
}
La un moment dat, vom dori sa aflam codul agentului. In cazul in care valoarea va fi null, vom putea atribui o alta valoare.
int id = a.Id;
int code = a.Code ?? -1;
Operatorul reprezinta o versiune mai compacta a instructiunii if else.
int? code = a.Code;
if(!code.HasValue)
code = -1;
Conversii
Conversia de la un tip generic, T, la un tip generic nullable, T?, este implicita.
Conversia de la T? la T necesita un cast.
int? a = 100;
int b = 300;
//implicit
a = b;
//explicit
b = (int)a;
Observatie:
In cazul in care a va fi null, vom primi o exceptie de tipul InvalidOperationException.
int? a = null;
int b = 300;
//InvalidOperationException
b = (int)a;
Boxing/Unboxing
Despre boxing stim ca permite ca tipurile valoare sa fie tratate ca obiecte, iar unboxing converteste in mod explicit tipul obiect la tipul valoare.
In cazul tipurilor nullable, atunci cand se “impacheteaza” T?, valoarea copiata pentru noul obiect va fi T, nu T?. Acest lucru este datorat faptului ca o valoare “impachetata” poate fi null pentru ca este de tip referinta. Unboxing pentru T? poate fi facut cu ajutorul operatorului as. In cazul in care conversia nu se incheie cu succes, returneaza null.
object a = "object";
int? b;
//null
b = a as int?;
//false
Console.WriteLine(b.HasValue);
Cel mai intalnit scenariu in folosirea nullable este in lucrul cu bazele de date, atunci cand o entitate este “mapata” pe o tabela ce are coloane definite ca nullable.