當前位置:
首頁 > 知識 > 細說Nullable類型

細說Nullable類型


目錄
一、簡介
二、語法和用法
三、類型的轉換和運算
四、裝箱與拆箱
五、GetType方法
六、ToString方法
七、System.Nullable幫助類
八、語法糖

一、簡介

眾所周知,值類型變數不能null,這也是為什麼它們被稱為值類型。但是,在實際的開發過程中,也需要值為null的一些場景。例如以下場景:

場景1:您從資料庫表中檢索可空的整數數據列,資料庫中的null值沒有辦法將此值分配給C#中Int32類型;

場景2:您在UI綁定屬性,但是某些值類型的欄位不是必須錄入的(例如在人員管理中的死亡日期);

場景3:在Java中,java.Util.Date是一個引用類型,因此可以將此類型的欄位設置為null。但是,在CLR中,System.DateTime是一個值類型,DateTime 變數不能null。如果使用Java編寫的應用程序要將日期/時間傳達給在CLR上運行的Web服務,如果Java應用程序發送是null, CLR中沒有供對應的類型;

場景4:在函數中傳遞值類型時,如果參數的值無法提供並且不想傳遞,可以使用默認值。但有時默認值並不是最佳的選擇,因為默認值實際也傳遞了一個默認的參數值,邏輯需要特殊的處理;

場景5:當從xml或json反序列化數據時,數據源中缺少某個值類型屬性的值,這種情況很不方便處理。

當然,我們日常工作中還有很多類似的情況。

為了擺脫這些情況,Microsoft在CLR中增加了可為空值類型的概念。為了更清楚理解這一點,我們看一下System.Nullable類型的邏輯定義:

1 namespace System
2 {
3 [Serializable]
4 public struct Nullable where T : struct
5 {
6 private bool hasValue;
7 internal T value;
8
9 public Nullable(T value) {
10 this.value = value;
11 this.hasValue = true;
12 }
13
14 public bool HasValue {
15 get {
16 return hasValue;
17 }
18 }
19
20 public T Value {
21 get {
22 if (!HasValue) {
23 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue);
24 }
25 return value;
26 }
27 }
28
29 public T GetValueOrDefault {
30 return value;
31 }
32
33 public T GetValueOrDefault(T defaultValue) {
34 return HasValue ? value : defaultValue;
35 }
36
37 public override bool Equals(object other) {
38 if (!HasValue) return other == null;
39 if (other == null) return false;
40 return value.Equals(other);
41 }
42
43 public override int GetHashCode {
44 return HasValue ? value.GetHashCode : 0;
45 }
46
47 public override string ToString {
48 return HasValue ? value.ToString : "";
49 }
50
51 public static implicit operator Nullable(T value) {
52 return new Nullable(value);
53 }
54
55 public static explicit operator T(Nullable value) {
56 return value.Value;
57 }
58 }
59 }

查看Nullable的定義

從上面的定義可以總結如下幾點:

  • Nullable 類型也是一個值類型;
  • Nullable 類型包含一個Value屬性用於表示基礎值,還包括一個Boolean類型的HasValue屬性用於表示該值是否為null
  • Nullable 是一個輕量級的值類型。Nullable類型的實例佔用內存的大小等於一個值類型與一個Boolean類型佔用內存大小之和;
  • Nullable 的泛型參數T必須是值類型。您只能將Nullable類型與值類型結合使用,您也可以使用用戶定義的值類型。

二、語法和用法

使用Nullable類型,只需指定一個其它值類型的泛型參數T。

示例:

1 Nullable i = 1;
2 Nullable j = null;
3 Nullable> k; //這是一個錯誤語法,編譯會報錯。

CLR還提供了一種簡寫的方式。

1 int? i = 1;
2 int? j = null;

可以通過 Value 屬性來獲取基礎類型的值。如下所示,如果不為null,則將返回實際的值,否則將拋出InvalidOperationException異常;您可以在調用Value屬性的時,需要檢查是否為null

1 Nullable i = 1;
2 Nullable j = null;
3
4 Console.WriteLine(i.HasValue);
5 //輸出結果:True
6
7 Console.WriteLine(i.Value);
8 //輸出結果:1
9
10 Console.WriteLine(j.HasValue);
11 //輸出結果:False
12
13 Console.WriteLine(j.Value);
14 //拋異常: System.InvalidOperationException

三、類型的轉換和運算

C#還支持簡單的語法來使用Nullable類型。它還支持Nullable實例的隱式轉換和轉換。如下示例演示:

1 // 從System.Int32隱式轉換為Nullable
2 int? i = 5;
3
4 // 從"null"隱式轉換為Nullable
5 int? j = null;
6
7 // 從Nullable到Int32的顯式轉換
8 int k = (int)i;
9
10 // 基礎類型之間的轉換
11 Double? x = 5; // 從Int到Nullable 的隱式轉換
12 Double? y = j; // 從Nullable 隱式轉換Nullable

對Nullable 類型使用操作符,與包含的基礎類型使用方法相同。

  • 一元運算符(++、--、 - 等),如果Nullable類型值是null時,返回null
  • 二元運算符(+、-、*、/、%、^等)任何操作數是null,返回null
  • 對於==運算符,如果兩個操作數都是null,則表達式計算結果為true,如果任何一個操作數是null,則表達式計算結果為false;如果兩者都不為null,它照常比較。
  • 對於關係運算符(>、<、>=、<=),如果任何一個操作數是null,則運算結果是false,如果操作數都不為null,則比較該值。

見下面的例子:

1 int? i = 5;
2 int? j = null;
3
4 // 一元運算符
5 i++; // i = 6
6 j = -j; // j = null
7
8 // 二元運算符
9 i = i + 3; // i = 9
10 j = j * 3; // j = null;
11
12 // 等號運算符(==、!=)
13 var r = i == null; //r = false
14 r = j == null; //r = true
15 r = i != j; //r = true
16
17 // 比較運算符(<、>、<=、>=)
18 r = i > j; //r = false
19
20 i = null;
21 r = i >= j; //r = false,注意,i=null、j=null,但是>=返回的結果是false

Nullable也可以像引用類型一樣,支持三元操作符。

1 // 如果僱員的年齡返回null(出生日期可能未輸入),請設置值0.
2 int age = employee.Age ?? 0;
3
4 // 在聚合函數中使用三元操作符。
5 int? numbers = {};
6 int total = numbers.Sum ?? 0;

四、裝箱與拆箱

我們已經知道了Nullable是一個值類型,現在我們再來聊一聊它的裝箱與拆箱。

CLR採用一個特殊的規則來處理Nullable類型的裝箱與拆箱。當一個Nullable類型的實例裝箱時,CLR會檢查實例的HasValue屬性:如果是true,則將實例Value屬性的值進行裝箱後返回結果;如果返回false,則直接返回null,不做任何的處理。

在拆箱處理時,與裝箱處反。CLR會檢查拆箱的對象是否為null,如果是直接創建一個新的實例 new Nullable,如果不為null,則將對象拆箱為類型T,然後創建一個新實例 new Nullable(t)。

1 int? n = null;
2 object o = n; //不會進行裝箱操作,直接返回null值
3
4 Console.WriteLine("o is null = {0}", object.ReferenceEquals(o, null));
5 //輸出結果:o is null = True
6
7
8 n = 5;
9 o = n; //o引用一個已裝箱的Int32
10
11 Console.WriteLine("o"s type = {0}", o.GetType);
12 //輸出結果:o"s type = System.Int32
13
14 o = 5;
15
16 //將Int32類型拆箱為Nullable類型
17 int? a = (Int32?)o; // a = 5
18 //將Int32類型拆箱為Int32類型
19 int b = (Int32)o; // b = 5
20
21 // 創建一個初始化為null
22 o = null;
23 // 將null變為Nullable類型
24 a = (Int32?)o; // a = null
25 b = (Int32)o; // 拋出異常:NullReferenceException

五、GetType方法

當調用Nullable類型的GetType方法時,CLR實際返回類型的是泛型參數的類型。因此,您可能無法區分Nullable實例上是一個Int32類型還是Nullable。見下面的例子:

1 int? i = 10;
2 Console.WriteLine(i.GetType);
3 //輸出結果是:System.Int32
4
5 i = null;
6 Console.WriteLine(i.GetType); //NullReferenceException

原因分析:

這是因為調用GetType方法時,已經將當前實例進行了裝箱,根據上一部分裝箱與拆箱的內容,這裡實際上調用的是Int32類型的GetType方法。

調用值類型的GetType方法時,均會產生裝箱,關於這一點大家可以自己去驗證。

六、ToString方法

當調用Nullable類型的ToString方法時,如果HasValue屬性的值為false,則返回String.Empty,如果該屬性的值為true,則調用的邏輯是Value.ToString。 見下面的例子:

1 int? i = 10;
2 Console.WriteLine(i.ToString);
3 //輸出結果:10
4
5 i = null;
6 Console.WriteLine(i.ToString == string.Empty);
7 //輸出結果:True

七、System.Nullable幫助類

微軟還提供一個同名System.Nullable的靜態類,包括三個方法:

1 public static class Nullable
2 {
3 //返回指定的可空類型的基礎類型參數。
4 public static Type GetUnderlyingType(Type nullableType);
5
6 //比較兩個相對值 System.Nullable 對象。
7 public static int Compare(T? n1, T? n2) where T : struct
8
9 //指示兩個指定 System.Nullable 對象是否相等。
10 public static bool Equals(T? n1, T? n2) where T : struct
11 }

在這裡面我們重點說明一下GetUnderlyingType(Type nullableType)方法,另外兩個方法是用來比較值的,大家可以自己研究。

GetUnderlyingType(Type nullableType)方法是用來返回一個可為空類型的基礎類型,如果nullableType參數不是一個封閉的Nullable泛型,則反回null

1 Console.WriteLine(Nullable.GetUnderlyingType(typeof(Nullable)));
2 //輸出結果:System.Int32
3
4 Console.WriteLine(Nullable.GetUnderlyingType(typeof(Nullable<>)) == null);
5 //輸出結果:True
6
7 Console.WriteLine(Nullable.GetUnderlyingType(typeof(int)) == null);
8 //輸出結果:True
9
10 Console.WriteLine(Nullable.GetUnderlyingType(typeof(string)) == null);
11 //輸出結果:True

八、語法糖

微軟對Nullable提供了豐富的語法糖來減少開發員的工作量,下面是我想到供您參考。


簡寫 編譯後的語句
1 int? i = 5; 2 3 int? j = null; 4 5 var r = i != null; 6 7 var v = (int) i; 8 9 i++; 10 11 i = i + 3; 12 13 r = i != j; 14 15 r = i >= j; 16 17 var k = i + j; 18 19 double? x = 5; 20 21 double? y = j; 1 int? i = new int?(5); 2 3 int? j = new int?; 4 5 var r = i.HasValue; 6 7 var v = i.Value; 8 9 i = i.HasValue ? new int?(i.GetValueOrDefault + 1) : new int?; 10 11 i = i.HasValue ? new int?(i.GetValueOrDefault + 3) : new int?; 12 13 r = i.GetValueOrDefault != j.GetValueOrDefault || i.HasValue != j.HasValue; 14 15 r = i.GetValueOrDefault >= j.GetValueOrDefault && i.HasValue & j.HasValue; 16 17 int? k = i.HasValue & j.HasValue ? new int?(i.GetValueOrDefault + j.GetValueOrDefault) : new int?; 18 19 double? x = new double?((double) 5); 20 21 double? y = j.HasValue ? new double?((double) j.GetValueOrDefault) : new double?;

參考:

  • https://www.codeproject.com/Articles/11854/C-Nullable-Types
  • https://www.codeproject.com/Articles/275471/Nullable-Types-in-Csharp-Net
  • https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/using-nullable-types
  • https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/index

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 達人科技 的精彩文章:

Javascript 「繼承」
使用CoApp創建NuGet C++靜態庫包
大話Python正則表達式
Linux 最新SO_REUSEPORT特性
java中方法總結

TAG:達人科技 |

您可能感興趣

iOS 12的新功能「Screen Time」的詳細說明!
蘋果支持文檔詳細說明Apple Card返現規則
和「植物殺手」 say goodbye 細說如何養好植物的那些事篇一
PC版Xbox遊戲通行證 全新Win10 Xbox APP功能詳細說明
細說win7和win10哪個好!
iPad Pro機身為什麼會彎曲?蘋果給出詳細說明~
7月6日@北京SKP RENDEZ-VOUS:細說芭蕾之美華麗Talk
細說A-Level,請耐心看完。不懂再問我
細說LVS-DR之VIP、DIP跨網段實例
山東如意或將「半價」收購Topshop母公司?細說背後緣由
4月27日 報名【華麗Talk】@北京SKP RENDEZ-VOUS:細說小眾香水的前世今生+全球奢侈茶葉品牌TWG品鑒
報名:4月27日【華麗Talk】細說小眾香水的前世今生@北京SKP RENDEZ-VOUS
3D虎細說3DTALK 系列產品的發展史
4月27日 報名華麗Talk@北京SKP RENDEZ-VOUS:細說小眾香水的前世今生+全球奢侈茶葉品牌TWG品鑒
報名:4月27日華麗Talk細說小眾香水的前世今生@北京SKP RENDEZ-VOUS
Lucene倒排索引簡述 細說倒排索引構建
細說「去中心化金融」系列一:De.Fi概念的緣起和分類
微軟官方有關Xbox One為開發者加入滑鼠和鍵盤支持的詳細說明
連續三年每年業績翻番!摩納哥時尚珠寶品牌 APM Monaco 家族掌門人細說奧秘
vivo NEX發布會上都沒有細說的黑科技,被扒出來了!