Indiscutiblement, els nuls són una de les pitjors malsons de l’desenvolupador, i causa d’una gran quantitat d’errors en temps d’execució d’aplicacions. La manca d’eines per tractar-apropiadament fa de vegades complexa la convivència amb l’ovella negra de l’ramat dels valors possibles a aplicar a una variable. En aquest article de el Manual de .NET tractarem de donar una mica de llum i explicar-les solucions que la tecnologia ens ofereix per conviure amb els valors null.
I és que amb poc que hagueu desenvolupat, segur que alguna vegada heu trobat la difícil tasca d’assignar un valor nul a una variable de tipus valor (int, float, char …) on no encaixa més que usant estratagemes difícils de realitzar, traçar i documentar. Per exemple, donada una classe Persona amb un propietat de tipus sencer anomenada Edat, què passa si carreguem un objecte d’aquesta classe des d’una base de dades si en aquesta el camp no era obligatori?
A priori, fàcil: a l’llegir de la base de dades comprovem si és nul, i en aquest cas li assignem a la propietat de la classe el valor -1. Bon pegat, sens dubte.
No obstant això, optar de forma general per aquesta idea presenta diversos inconvenients. En primer lloc, per ser fidels a la realitat, si volguéssim emmagatzemar de nou aquest objecte a la base de dades, caldria realitzar el canvi invers, és a dir, comprovar si l’edat és -1 i en aquest cas guardar al camp un nul.
en segon lloc, fixeu-vos que estem portant a la classe artificis que no tenen sentit en el domini de el problema a resoldre, en l’entitat a la qual representa. Mirant-una mica de lluny, quin sentit té una edat negativa en una entitat Persona? Cap.
En tercer lloc, hi ha un problema de coherència en les consultes. Si tinc en memòria una col·lecció de persones (realitzada, per exemple utilitzant tipus genèrics) i vull conèixer les que tenen edat definida, hauria de comprovar per cada element si la seva propietat Edat val -1; però, a l’realitzar la mateixa consulta a la base de dades hauria de preguntar pel valor NULL sobre el camp corresponent.
És cert que podríem portar també a la base de dades el concepte “-1 vol dir nul” en el camp Edat, però … no estaríem esquitxant l’estructura de dades amb una particularitat (i limitació) de el llenguatge de programació utilitzat? Una altra idea estranya podria ser introduir l’edat en un string i problema solucionat: les cadenes, a l’ésser un tipus referència poden contenir nuls sense problemes, però cal reconèixer que les ordenacions sortirien regular; -)
Per Finalment, ¿i si en comptes de l’edat, on clarament no poden existir negatius estiguéssim parlant de la classe CompteCorrent i la seva propietat SaldoActual? Aquí sí que es veu clarament que utilitzar negatius no és un recurs vàlid.
Solucions, a part de les comentades, n’hi ha per tots els gustos. Es podria, per exemple, afegir una propietat booleana paral·lela que indiqués si el camp Edat és nul (una feinada extra, sobretot si són diverses les propietats que poden tenir aquests valors), o encapsular l’edat dins d’una classe que incorporés la lògica de tractament d’aquest nul.
En qualsevol cas, les solucions possibles són treballoses, de vegades complexes, i sobretot, massa artificials per tractar-se d’una cosa tan quotidiana com és un simple camp nul.
Conscients d’això, els dissenyadors de C # van tenir en compte ja en la seva versió 2.0 una interessant característica: els nullables types, o tipus anul·lables, un mecanisme que permet introduir el nul en les nostres vides de manera no traumàtica.
La següent línia generava un error en compilació, que deia, i no li faltava raó, que “no es pot convertir null a ‘int’ perquè és un tipus de valor”:
int s = null;
Des de C # 2.0, és possible fer el següent:
int? s; a s = null; a s = 1;
Cal observar la interrogació al costat de l’tipus, que és l’indicatiu que la variable s és de tipus sencer, però que admet també un valor nul.
Per dins, això funciona de la següent manera: int? és un àlies de l’tipus genèric System.Nullable. De fet, podríem usar qualsevol de les dues formes d’expressar-ho. Internament es crea una estructura amb dues propietats de només lectura: HasValue, que retorna si la variable en qüestió té valor, i Value, que conté el valor en si.
S’entén que una variable amb HasValue igual a false conté el valor nul, i si intentem accedir a aquest a través de Value, es llançarà una excepció.
No obstant això, el principal avantatge que tenen és que s’utilitzen igual que si fos un tipus valor tradicional. Els tipus anul·lables es comporten pràcticament com ells i ofereixen els mateixos operadors, encara que cal tenir en compte les seves particularitats, com s’aprecia en el següent codi:
int?a = 1; a int? b = 2; a int? intNulo = null; a bool? si = true; a bool? no = false; a bool? niSiNiNo = null; a Console.WriteLine (a + b); // 3 de Console.WriteLine (a + intNulo); // Res, és nul a Console.WriteLine (a * intNulo); // Res, és nul a Console.WriteLine (si & no); // false a Console.WriteLine (si & no); // true a Console.WriteLine (si & niSiNiNo); // Res, és nul a Console.WriteLine (no & niSiNiNo); // false a Console.WriteLine (si | niSiNiNo); // true a Console.WriteLine (no | niSiNiNo); // Res, és nul
Podem crear tipus anul·lables de qualsevol tipus valor de l’framework: int ?, float ?, double ?, char ?, etc.
I de fet, com a tipus valor que són, també podem fer-ho amb enumeracions. El següent exemple seria incorrecte:
Estat Estat1 = Estado.Correcto; de l’Estat estado2 = null;
La segona línia provoca l’error en compilació “No es pot convertir null en ‘ConsoleApplication1.Program.Estado’ perquè és un tipus de valor “. Lògic, és igual que si intentéssim assignar el maleït valor nul a un integer.
Això pot causar lleugeres molèsties per a la gestió de valors “indeterminats”, ja que estaríem obligats, per exemple, a utilitzar un altre membre de l’enumeració per representar aquest valor, com en el següent codi en Visual Basic .Net:
Public Enum Estat d’Indeterminat = 0 a Correcte = 1 de Incorrecte = 2 de End enum
No obstant això, hi ha ocasions en què resulta més interessant poder disposar de la valor nul com una opció per a les variables de l’tipus de la nostra enumeració. Per a això, podem utilitzar la mateixa sintaxi que hem vist anteriorment:
‘VB.NET a Dim Estat1 As Nullable (Of Estat) a Dim estado2 As Nullable (Of Estat) a Estat1 = Estado.Correcto a estado2 = Nothing // C # de l’Estat? Estat1 = Estado.Correcto; de l’Estat? estado2 = null;