本文資料引用自李建忠先生的演講稿
C# 引入Nullable類型,允許我們將實值型別賦值為null。
其實質上是在Framework中增加了一個Nullable<T>的泛型結構類型。
[SerializableAttribute()]
public struct Nullable<T> : IFormattable, IComparable, INullableValue
where T : ValueType
看看上面的聲明,我們可以確定,Nullable是一個實值型別,而且限制型別參數為實值型別。
另外C#在文法層面作了簡化,見下面的代碼。
int? x = 3;
Nullable<int> y = new Nullable<int>(3);
看看反編譯的結果。
Nullable<int> nullable1 = new Nullable<int>(3);
Nullable<int> nullable2 = new Nullable<int>(3);
實際上,編譯器會將縮寫方式處理成完整的結構體建立代碼。
Nullable包含兩個有用的屬性,HasValue用來判斷類型是否為空白,如果不為空白則可以通過Value屬性擷取其基礎類型的值。
當其值不為空白時,可以直接使用其基礎類型的運算子進行操作。GetValueOrDefault 方法可以擷取值或者基礎類型的預設值。
int? x = 3;
int y = 1;
Console.WriteLine(x + y);
不過看看這段代碼的反編譯結果,你可能會發現一些問題。
Nullable<int> nullable1 = new Nullable<int>(3);
int num1 = 1;
Nullable<int> nullable2 = nullable1;
int num2 = num1;
Console.WriteLine(nullable2.get_HasValue() ? new Nullable<int>(nullable2.GetValueOrDefault() + num2) : new Nullable<int>());
原本簡單的代碼變得很複雜,編譯器建立了新的Nullable對象,而且通過判斷,如發現Nullable對象為空白,則放棄加法操作,直接返回空。
繼續看反編譯的IL代碼,還出現了box指令,因此Nullable類型的代價是很高的,如非必須,不要使用。
.entrypoint
// Code Size: 63 byte(s)
.maxstack 3
.locals init (
[mscorlib]System.Nullable`1<int32> nullable1,
int32 num1,
[mscorlib]System.Nullable`1<int32> nullable2,
int32 num2,
[mscorlib]System.Nullable`1<int32> nullable3)
L_0000: nop
L_0001: ldloca.s nullable1
L_0003: ldc.i4.3
L_0004: call instance void [mscorlib]System.Nullable`1<int32>::.ctor(!0)
L_0009: nop
L_000a: ldc.i4.1
L_000b: stloc.1
L_000c: ldloc.0
L_000d: stloc.2
L_000e: ldloc.1
L_000f: stloc.3
L_0010: ldloca.s nullable2
L_0012: call instance bool [mscorlib]System.Nullable`1<int32>::get_HasValue()
L_0017: brtrue.s L_0025
L_0019: ldloca.s nullable3
L_001b: initobj [mscorlib]System.Nullable`1<int32>
L_0021: ldloc.s nullable3
L_0023: br.s L_0033
L_0025: ldloca.s nullable2
L_0027: call instance !0 [mscorlib]System.Nullable`1<int32>::GetValueOrDefault()
L_002c: ldloc.3
L_002d: add
L_002e: newobj instance void [mscorlib]System.Nullable`1<int32>::.ctor(!0)
L_0033: box [mscorlib]System.Nullable`1<int32>
L_0038: call void [mscorlib]System.Console::WriteLine(object)
L_003d: nop
L_003e: ret
最後提一下 ?? 這個操作符。
The ?? defines a default value that is returned when a nullable type is assigned to a non-nullable type.
// ?? operator example.
int? x = null;
int y = x ?? -1; // Assigns y to -1 if x is null.
相當於
int y = x.HasValue ? x.GetValueOrDefault() : -1;