A null kulcsszó C# esetén egy olyan referencia értéket jelent, amely nem meghatározott, vagyis nincs értéke. Csak referencia típusoknál alkalmazható. Az összes referencia típus inicializálás előtt null értéket vesz fel. Az alábbi rövid példa a null használatát mutatja be:
var x = new SajatOsztaly();
x = null;
Érték típusok esetén a null nem alkalmazható, mivel ezen típusok magát az értéket tárolják és nem egy referenciát. Előfordulhat azonban, hogy szeretnénk érték típus esetén is definiálni egy nem definiálható állapotot. Különösen hasznos lehet ez a bool típus esetén. Ebben az esetben nem kell külön osztályt alkotnunk, ami redundáns módon másolja a bool típus funkcionalitását. Elég a típus deklarációt kiegészítenünk a kérdőjel karakterrel.
A típus név után elhelyezett kérdőjel bármilyen érték típust átalakít nullable típusúra. Példa:
bool? nullazhato = null;
Ha értéket szeretnénk a típusunkba betenni, akkor a szükséges konverziók implicit módon megtörténnek:
nullazhato = true; //gond nélkül megy
Azonban ha értéket szeretnénk kiolvasni, akkor explicit módon ezt jelezni kell a fordítónak. Az explicit jelzés azért szükséges, mert a nullable típusok esetén, ha megszüntetjük a null kitételt, akkor a típus alapértelmezett értéke kerül be, ami explicit jelzés nélkül programozói hibákhoz vezethet.
//bool valami = nullazhato - hibát produkál fordításkor
bool valami = (bool)nullazhato; //helyes, a változó értéke false lesz.
bool masik = nullazhato.Value; //szintén helyes
if (nullazhato.HasValue) {
//ellenőrizzük, hogy egyáltalán tárol-e értéket.
}
A nullázható típus átalakítás úgy működik egyébként, hogy a fordító a jelzett struktúrát becsomagolja egy Nullable<T> struktúrába, ami a tárolt értéken kívül tartalmaz egy megvalósítást, hogy null értéket is adhassunk.
Problémák akkor adódnak, ha egy null értékű változón metódust vagy tulajdonságot szeretnénk meghívni. Ilyen esetben, ha a változónak null az értéke, akkor futás időben NullReferenceException hibát kapunk. Az alábbi példa a null helyes kezelését mutatja be különböző módokon:
using System;
namespace PeldaNullKezeles
{
class Egyszeru
{
private int _szam;
public Egyszeru() { _szam = 42; }
public void Metodus()
{
Console.WriteLine("A tárolt számom: {0}", _szam);
}
}
class Program
{
static void Main(string[] args)
{
Egyszeru valtozo = null;
//futás idejű hiba
//valtozo.Metodus();
//korrekt kezelés #1
if (valtozo != null) valtozo.Metodus();
//korrekt kezelés #2
valtozo?.Metodus();
//Nem korrekt kezelés:
try
{
valtozo.Metodus();
}
catch (NullReferenceException) { }
//nagyon nem korrekt kezelés:
try
{
valtozo.Metodus();
}
catch (Exception) { }
Console.ReadKey();
}
}
}
A korrekt kezelés #2 részben használt operátor a null kondíció vizsgálatára szolgál, A C# 6.0-ás verziójában mutatkozott be. Ugyanazt csinálja, mint a korrekt kezelés #1 részben a kód, viszont sokkal átláthatóbb lesz tőle a program, főleg akkor, ha több, egymásba ágyazott osztályon kell vizsgálni a null kritériumot.
A nagyon nem korrekt kezelés részben említett példát igyekezzünk elkerülni. A probléma ezzel a kódrészlettel az, hogy minden hibát elhallgat és csendesen “elkezel”. Az ilyen megoldások csak arra jók, hogy az egzotikus hibák felderítését és javítását megnehezítsék. Kivételkezelés esetén általános szabály, hogy csak olyan hibákat kapjunk el a kezelt részben, amilyen hibákra számítunk.