細說Nullable<T>類型

来源:http://www.cnblogs.com/tdfblog/archive/2017/06/27/Nullable-Types-in-Csharp-Net.html
-Advertisement-
Play Games

一、簡介 眾所周知,值類型變數不能null,這也是為什麼它們被稱為值類型。但是,在實際的開發過程中,也需要值為null的一些場景。例如以下場景: 場景1:您從資料庫表中檢索可空的整數數據列,資料庫中的null值沒有辦法將此值分配給C#中Int32類型; 場景2:您在UI綁定屬性,但是某些值類型的欄位 ...


目錄
一、簡介
二、語法和用法
三、類型的轉換和運算
四、裝箱與拆箱
五、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<T>類型的邏輯定義:

 1 namespace System
 2 {
 3     [Serializable]
 4     public struct Nullable<T> 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>(T value) {
52             return new Nullable<T>(value);
53         } 
54 
55         public static explicit operator T(Nullable<T> value) { 
56             return value.Value; 
57         }
58     }
59 } 
查看Nullable的定義

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

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

 

二、語法和用法

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

  示例: 

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

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

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

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

 1     Nullable<int> i = 1;
 2     Nullable<int> 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<T>類型。它還支持Nullable<T>實例的隱式轉換和轉換。如下示例演示:

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

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

  • 一元運算符(++、--、 - 等),如果Nullable<T>類型值是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<T>也可以像引用類型一樣,支持三元操作符。

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

 

四、裝箱與拆箱

  我們已經知道了Nullable<T>是一個值類型,現在我們再來聊一聊它的裝箱與拆箱。
  CLR採用一個特殊的規則來處理Nullable<T>類型的裝箱與拆箱。當一個Nullable<T>類型的實例裝箱時,CLR會檢查實例的HasValue屬性:如果是true,則將實例Value屬性的值進行裝箱後返回結果;如果返回false,則直接返回null,不做任何的處理。
  在拆箱處理時,與裝箱處反。CLR會檢查拆箱的對象是否為null,如果是直接創建一個新的實例 new Nullable<T>(),如果不為null,則將對象拆箱為類型T,然後創建一個新實例 new Nullable<T>(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<Int32>類型
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<Int32>類型
24     a = (Int32?)o; // a = null 
25     b = (Int32)o; // 拋出異常:NullReferenceException

 

五、GetType()方法

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

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<T>類型的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<T> 對象。
 7     public static int Compare<T>(T? n1, T? n2) where T : struct
 8 
 9     //指示兩個指定 System.Nullable<T> 對象是否相等。
10     public static bool Equals<T>(T? n1, T? n2) where T : struct
11 }

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

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

 1     Console.WriteLine(Nullable.GetUnderlyingType(typeof(Nullable<int>)));
 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<T>提供了豐富的語法糖來減少開發員的工作量,下麵是我想到供您參考。

簡寫 編譯後的語句
 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?();
 

參考:

 

轉載請註明出處,原文鏈接:http://www.cnblogs.com/tdfblog/p/Nullable-Types-in-Csharp-Net.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 下拉框聯動效果,我們以部門--職位為例,選擇部門時,關聯到該部門的職位.下拉框的寫法就不多說了,詳細請參照前文. 視圖: 其中,dept是部門的屬性,deptlist是部門下拉框的屬性,job是職位的屬性,joblist是職位下拉框的屬性,下拉框綁定請參照前文 當部門變動的時候,職位也相應改變: 執 ...
  • using System; using System.Collections; using System.IO; using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Drawing2D; namespace ...
  • using System.Web; using System.Configuration; namespace DotNet.Utilities { public class VideoConvert : System.Web.UI.Page { public VideoConvert() { } ...
  • 1.Model 2.cotroller 3.View ...
  • 可進行FTP的上傳,下載等其他功能,支持斷點續傳: using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; usi ...
  • 1.Model 2.controller (1)先寫一個程式綁定,可以通過資料庫綁定或者直接綁定 (2)初始化,並傳給視圖 3.視圖 ...
  • 下圖為一個已經創建好的資料庫表關係 實體數據模型的創建過程 在Visual Studio項目中,右鍵程式集菜單,選擇【添加】 》【新建項】,在【添加新項視窗】中選擇【ADO.NET實體數據模型】,如下圖 在【實體數據模型嚮導】視窗中選擇【來自資料庫的EF設計器】 然後選擇資料庫連接,如果沒有的話,可 ...
  • 2017 06 28 更新: OptionsSnapshot 已改為 OptionsManager "變更詳情" IOptionsCache 已改為 IOptionsMonitorCache "變更詳情" 在 "上一章" 中,介紹了 IOptions 的使用, 而我們知道,在 Configurati ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...