.NET Nullable Value Types
C# programlamada, veri türleri çok önemlidir. Ancak, bazen bir değişkenin değeri bilinmeyen veya atanmamış olabilir. İşte burada “Nullable<T>” tipi devreye girer. “Nullable<T>” tipi, değeri olmayan veya tanımsız olan bir değişkeni temsil etmek için kullanılır. Bu makalede, “Nullable<T>” tipinin nasıl kullanılacağını, neden bu kadar önemli olduğunu ve gerçek dünya uygulamalarında nasıl fayda sağlayabileceğini keşfedeceğiz.
Bilindiği gibi değer tipleri null olamaz. Herhangi bir değer set edilmediği zaman default değer alırlar. Fakat bazı durumlarda değer tiplerinin null olması gerekebilir. Örneğin veritabanı işlemlerinde bir tane tabloda Int32 data tipini tutan bir kolon olsun. .NET Framework tarafında bu değeri karşılamak zor olacktır. Çünkü CLR’in Int32 yi null olarak temsil edeceği bir mekanızması yok. Bu sebeplerden dolayı Microsoft Nullable tip konseptini CLR’a ekledi. Bu konseptin nasıl çalıştığını anlamak için Framework Class Library(FCL)’ye eklenen System.Nullable<T> struct tipine bakmak gerekir. Aşağıda tipin örneği mevcut.
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct Nullable<T> where T : struct
{
// These 2 fields represent the state
private Boolean hasValue = false; // Assume null
internal T value = default(T); // Assume all bits zero
public Nullable(T value)
{
this.value = value;
this.hasValue = true;
}
public Boolean HasValue { get { return hasValue; } }
public T Value
{
get {
if (!hasValue) {
throw new InvalidOperationException("Nullable object must have a value.");
}
return value;
}
}
public T GetValueOrDefault() { return value; }
public T GetValueOrDefault(T defaultValue)
{
if (!HasValue) return defaultValue;
return value;
}
public override Boolean Equals(Object other)
{
if (!HasValue) return (other == null);
if (other == null) return false;
return value.Equals(other);
}
public override int GetHashCode()
{
if (!HasValue) return 0;
return value.GetHashCode();
}
public override string ToString()
{
if (!HasValue) return "";
return value.ToString();
}
public static implicit operator Nullable<T>(T value)
{
return new Nullable<T>(value);
}
public static explicit operator T(Nullable<T> value)
{
return value.Value;
}
}
Görüldüğü gibi bu tip, değer tipini null olsa bile sarmalıyor. Nullable<T> tipi kendisi değer tipli bir struct olduğu için sarmalanan değer tipi yine hafif bir şekilde belleğin Stack bölgesinde tutuluyor. Sarmaladığı değer tipinin adresteki yerine artı olarak bir boolean değerini adrese ekler. Zaten yukaridaki kod ta Nullable<T> tipindeki generic parametresi “where T : struct” ifadesi ile Struct’lara kısıtlanmıştır. Çünkü referens tipler zaten null olabiliyor.
Nullable<T> tipi kodta şu şekilde kullanılır.
Nullable<Int32> x = 5;
Nullable<Int32> y = null;
Console.WriteLine("x: HasValue={0}, Value={1}", x.HasValue, x.Value);
Console.WriteLine("y: HasValue={0}, Value={1}", y.HasValue, y.GetValueOrDefault());
Kodu derleyip çalıştırıldığında şu çıktı alınıyor.
x: HasValue=True, Value=5
y: HasValue=False, Value=0
C#’in Nullable<T> Tip Desteği
C# adil bir şekilde Nullable<T> tipini destekler. Fakat C# ekibi Nullable<T> desteğini dilin direk özelliği olarak eklemek istediler. Dolayısıyla C# ekibi daha kısa temiz bir yazım şekli olan “soru işareti” ekleme formunu dile ekledi. Int32? ile Nullable<Int32> birbirinin aynısını temsil eder.
Int32? x = 5;
Int32? y = null;
C#’s Null-Coalescing Operator
C#’ta null-coalesing operator(??) diye bir operator mevcut. Bu operator iki operand alır. Eğer soldaki operand null değilse, onun değeri döner. Ama soldaki operand null ise, sağdaki operand’in değeri döner. Null-coalesing operatörü değer tiplerinde çalıştığı gibi referans tiplerde de çalışmaktadır.
private static void NullCoalescingOperator()
{
Int32? b = null;
// The line below is equivalent to:
// x = (b.HasValue) ? b.Value : 123
Int32 x = b ?? 123;
Console.WriteLine(x); // "123"
// The line below is equivalent to:
// String temp = GetFilename();
// filename = (temp != null) ? temp : "Untitled";
String filename = GetFilename() ?? "Untitled";
}
CLR’in Null Olabilen Değer Tipleri İçin Özel Desteği
CLR default olarak null olabilen tipleri desteklemektedir. Bu destek null olan tipler arasında boxing,unboxing, GetType metodunu çağırma, interface metodlarını çağırma gibi işlemleri sorunsuzca yapabiliyor. Bu durum onları daha doğal ve çoğu geliştiricinin beklediği gibi davranmasını sağlıyor.
Null Olabilen Değer Tiplerinde Boxing İşlemi
Farz edelimki Nullable<Int32> tipinde bir değişken null değere sahip olsun. Eğer bu değişken bir metoda object tipinde parametre olarak geciliyor olsun. Bu durumda bu değişkenin object tipine boxing edilmesi gerekir. Tam buarada CLR Nullable<Int32> tipindeki değişkenin değerine bakar. Eğer değer null ise object tipine null geçilir ve boxing işlemi yapılmaz. Fakat değer null değilse bulunan değer boxing edilir.
// Boxing Nullable<T> is null or boxed T
Int32? n = null;
Object o = n; // o is null
Console.WriteLine("o is null={0}", o == null); // "True"
n = 5;
o = n; // o refers to a boxed Int32
Console.WriteLine("o's type={0}", o.GetType()); // "System.Int32"
Null Olabilen Değer Tiplerinde Unboxing İşlemi
CLR boxed edilmiş bir T değer tipini, unboxed olan T veya Nullable<T> tipine çevirebilir. Eğer boxed olan değer null ise ve unboxing işlemi Nullable<T> ye yapılıyorsa Nullable<T> değeri null olur.
// Create a boxed Int32
Object o = 5;
// Unbox it into a Nullable<Int32> and into an Int32
Int32? a = (Int32?) o; // a = 5
Int32 b = (Int32) o; // b = 5
// Create a reference initialized to null
o = null;
// "Unbox" it into a Nullable<Int32> and into an Int32
a = (Int32?) o; // a = null
b = (Int32) o; // NullReferenceException
Null Olabilen Değer Tiplerinde GetType Metodunu Çağırma İşlemi
Nullable<T> tipinde GetType metodu çağrıldığında, CLR aslında geriye Nullable<T> tipini değil Nullable<T> tipinin sarmaladığı gerçek olan T’yi döner.
Int32? x = 5;
// The line below displays "System.Int32"; not "System.Nullable<Int32>"
Console.WriteLine(x.GetType());
Null Olabilen Değer Tiplerinde Interface Metodlarını Çağırma İşlemi
Aşağıdaki kodta tipi Nullable<T> olan n değişkeninin IComparable<Int32> interface tipine cast ediliyor. Fakat normal şartlarda Int32 tipi IComparable<Int32> interface’ni implemente ederken Nullable<T> tipi etmiyor. C# compiler bir şekilde bu kodu derliyor.
Int32? n = 5;
Int32 result = ((IComparable) n).CompareTo(5); // Compiles & runs OK
Console.WriteLine(result); // 0
Ve CLR’in bu desteği olmasaydı kodu daha hantal ve uzun kod yazmak zorunda kalcaktık. Yani Nullable<T> tipinde bir değerin interface metodunu çağırmadan önce cast işlemi yapmak zorunda kalacaktık. Aşağıdaki örnekteki gibi.
Int32 result = ((IComparable) (Int32) n).CompareTo(5); // Cumbersome
Bu makalede “Nullable<T>” tipi hakkında temel bilgilere ve uygulama örneklerine göz attık. “Nullable<T>” tipi, C# programcılarına daha esnek ve güçlü kod yazma yeteneği sunar. Değişkenleri tanımsız veya boş olarak işaretlemek, programları daha sağlam hale getirebilir ve hata ayıklamayı kolaylaştırabilir. Umarım faydalı olmuştur.
Yararlanılan kaynaklar:
Microsoft CLR via C# Fourth Edition kitabı.