網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
一、簡(jiǎn)介
眾所周知,值類(lèi)型變量不能null,這也是為什么它們被稱(chēng)為值類(lèi)型。但是,在實(shí)際的開(kāi)發(fā)過(guò)程中,也需要值為null
的一些場(chǎng)景。例如以下場(chǎng)景:
場(chǎng)景1:您從數(shù)據(jù)庫(kù)表中檢索可空的整數(shù)數(shù)據(jù)列,數(shù)據(jù)庫(kù)中的null
值沒(méi)有辦法將此值分配給C#中Int32類(lèi)型;
場(chǎng)景2:您在UI綁定屬性,但是某些值類(lèi)型的字段不是必須錄入的(例如在人員管理中的死亡日期);
場(chǎng)景3:在Java中,java.Util.Date
是一個(gè)引用類(lèi)型,因此可以將此類(lèi)型的字段設(shè)置為null
。但是,在CLR中,System.DateTime
是一個(gè)值類(lèi)型,DateTime 變量不能null
。如果使用Java編寫(xiě)的應(yīng)用程序要將日期/時(shí)間傳達(dá)給在CLR上運(yùn)行的Web服務(wù),如果Java應(yīng)用程序發(fā)送是null
, CLR中沒(méi)有供對(duì)應(yīng)的類(lèi)型;
場(chǎng)景4:在函數(shù)中傳遞值類(lèi)型時(shí),如果參數(shù)的值無(wú)法提供并且不想傳遞,可以使用默認(rèn)值。但有時(shí)默認(rèn)值并不是最佳的選擇,因?yàn)槟J(rèn)值實(shí)際也傳遞了一個(gè)默認(rèn)的參數(shù)值,邏輯需要特殊的處理;
場(chǎng)景5:當(dāng)從xml或json反序列化數(shù)據(jù)時(shí),數(shù)據(jù)源中缺少某個(gè)值類(lèi)型屬性的值,這種情況很不方便處理。
當(dāng)然,我們?nèi)粘9ぷ髦羞€有很多類(lèi)似的情況。
為了擺脫這些情況,Microsoft在CLR中增加了可為空值類(lèi)型的概念。為了更清楚理解這一點(diǎn),我們看一下System.Nullable
類(lèi)型的邏輯定義:
namespace System { [Serializable] public struct Nullablewhere T : struct { private bool hasValue; internal T value; public Nullable(T value) { this.value = value; this.hasValue = true; } public bool HasValue { get { return hasValue; } } public T Value { get { if (!HasValue) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue); } return value; } } public T GetValueOrDefault() { return value; } public T GetValueOrDefault(T defaultValue) { return HasValue ? value : defaultValue; } public override bool Equals(object other) { if (!HasValue) return other == null; if (other == null) return false; return value.Equals(other); } public override int GetHashCode() { return HasValue ? value.GetHashCode() : 0; } public override string ToString() { return HasValue ? value.ToString() : ""; } public static implicit operator Nullable (T value) { return new Nullable (value); } public static explicit operator T(Nullable value) { return value.Value; } } }
從上面的定義可以總結(jié)如下幾點(diǎn):
- Nullable
類(lèi)型也是一個(gè)值類(lèi)型; - Nullable
類(lèi)型包含一個(gè)Value屬性用于表示基礎(chǔ)值,還包括一個(gè) Boolean
類(lèi)型的HasValue屬性用于表示該值是否為null
?; - Nullable
是一個(gè)輕量級(jí)的值類(lèi)型。Nullable 類(lèi)型的實(shí)例占用內(nèi)存的大小等于一個(gè)值類(lèi)型與一個(gè) Boolean
類(lèi)型占用內(nèi)存大小之和; - Nullable
的泛型參數(shù)T必須是值類(lèi)型。您只能將Nullable 類(lèi)型與值類(lèi)型結(jié)合使用,您也可以使用用戶(hù)定義的值類(lèi)型。
二、語(yǔ)法和用法
使用Nullable
示例:
Nullablei = 1; Nullable j = null; Nullable > k; //這是一個(gè)錯(cuò)誤語(yǔ)法,編譯會(huì)報(bào)錯(cuò)。
CLR還提供了一種簡(jiǎn)寫(xiě)的方式。
int? i = 1; int? j = null;
可以通過(guò) Value 屬性來(lái)獲取基礎(chǔ)類(lèi)型的值。如下所示,如果不為null
,則將返回實(shí)際的值,否則將拋出InvalidOperationException
異常;您可以在調(diào)用Value屬性的時(shí),需要檢查是否為null
。
Nullablei = 1; Nullable j = null; Console.WriteLine(i.HasValue); //輸出結(jié)果:True Console.WriteLine(i.Value); //輸出結(jié)果:1 Console.WriteLine(j.HasValue); //輸出結(jié)果:False Console.WriteLine(j.Value); //拋異常: System.InvalidOperationException
三、類(lèi)型的轉(zhuǎn)換和運(yùn)算
C#還支持簡(jiǎn)單的語(yǔ)法來(lái)使用Nullable
// 從System.Int32隱式轉(zhuǎn)換為Nullableint? i = 5; // 從'null'隱式轉(zhuǎn)換為Nullable int? j = null; // 從Nullable 到Int32的顯式轉(zhuǎn)換 int k = (int)i; // 基礎(chǔ)類(lèi)型之間的轉(zhuǎn)換 Double? x = 5; // 從Int到Nullable 的隱式轉(zhuǎn)換 Double? y = j; // 從Nullable 隱式轉(zhuǎn)換Nullable
對(duì)Nullable
- 一元運(yùn)算符(++、--、 - 等),如果Nullable
類(lèi)型值是 null
時(shí),返回null
; - 二元運(yùn)算符(+、-、*、/、%、^等)任何操作數(shù)是
null
,返回null
; - 對(duì)于==運(yùn)算符,如果兩個(gè)操作數(shù)都是
null
,則表達(dá)式計(jì)算結(jié)果為true
,如果任何一個(gè)操作數(shù)是null
,則表達(dá)式計(jì)算結(jié)果為false;如果兩者都不為null
,它照常比較。 - 對(duì)于關(guān)系運(yùn)算符(>、<、>=、<=),如果任何一個(gè)操作數(shù)是
null
,則運(yùn)算結(jié)果是false
,如果操作數(shù)都不為null
,則比較該值。
見(jiàn)下面的例子:
int? i = 5; int? j = null; // 一元運(yùn)算符 i++; // i = 6 j = -j; // j = null // 二元運(yùn)算符 i = i + 3; // i = 9 j = j * 3; // j = null; // 等號(hào)運(yùn)算符(==、!=) var r = i == null; //r = false r = j == null; //r = true r = i != j; //r = true // 比較運(yùn)算符(<、>、<=、>=) r = i > j; //r = false i = null; r = i >= j; //r = false,注意,i=null、j=null,但是>=返回的結(jié)果是false
Nullable
// 如果雇員的年齡返回null(出生日期可能未輸入),請(qǐng)?jiān)O(shè)置值0. int age = employee.Age ?? 0; // 在聚合函數(shù)中使用三元操作符。 int?[] numbers = {}; int total = numbers.Sum() ?? 0;
四、裝箱與拆箱
我們已經(jīng)知道了Nullable
CLR采用一個(gè)特殊的規(guī)則來(lái)處理Nullabletrue
,則將實(shí)例Value屬性的值進(jìn)行裝箱后返回結(jié)果;如果返回false
,則直接返回null
,不做任何的處理。
在拆箱處理時(shí),與裝箱處反。CLR會(huì)檢查拆箱的對(duì)象是否為null
,如果是直接創(chuàng)建一個(gè)新的實(shí)例 new Nullablenull
,則將對(duì)象拆箱為類(lèi)型T,然后創(chuàng)建一個(gè)新實(shí)例 new Nullable
int? n = null; object o = n; //不會(huì)進(jìn)行裝箱操作,直接返回null值 Console.WriteLine("o is null = {0}", object.ReferenceEquals(o, null)); //輸出結(jié)果:o is null = True n = 5; o = n; //o引用一個(gè)已裝箱的Int32 Console.WriteLine("o's type = {0}", o.GetType()); //輸出結(jié)果:o's type = System.Int32 o = 5; //將Int32類(lèi)型拆箱為Nullable類(lèi)型 int? a = (Int32?)o; // a = 5 //將Int32類(lèi)型拆箱為Int32類(lèi)型 int b = (Int32)o; // b = 5 // 創(chuàng)建一個(gè)初始化為null o = null; // 將null變?yōu)镹ullable 類(lèi)型 a = (Int32?)o; // a = null b = (Int32)o; // 拋出異常:NullReferenceException
五、GetType()方法
當(dāng)調(diào)用NullableGetType()
方法時(shí),CLR實(shí)際返回類(lèi)型的是泛型參數(shù)的類(lèi)型。因此,您可能無(wú)法區(qū)分Nullable
int? i = 10; Console.WriteLine(i.GetType()); //輸出結(jié)果是:System.Int32 i = null; Console.WriteLine(i.GetType()); //NullReferenceException
原因分析:
這是因?yàn)檎{(diào)用GetType()
方法時(shí),已經(jīng)將當(dāng)前實(shí)例進(jìn)行了裝箱,根據(jù)上一部分裝箱與拆箱的內(nèi)容,這里實(shí)際上調(diào)用的是Int32類(lèi)型的GetType()
方法。
調(diào)用值類(lèi)型的GetType()
方法時(shí),均會(huì)產(chǎn)生裝箱,關(guān)于這一點(diǎn)大家可以自己去驗(yàn)證。
六、ToString()方法
?當(dāng)調(diào)用NullableToString()
方法時(shí),如果HasValue屬性的值為false
,則返回String.Empty
,如果該屬性的值為true
,則調(diào)用的邏輯是Value.ToString()
。?見(jiàn)下面的例子:
int? i = 10; Console.WriteLine(i.ToString()); //輸出結(jié)果:10 i = null; Console.WriteLine(i.ToString() == string.Empty); //輸出結(jié)果:True
七、System.Nullable幫助類(lèi)
?微軟還提供一個(gè)同名System.Nullable
的靜態(tài)類(lèi),包括三個(gè)方法:?
public static class Nullable { //返回指定的可空類(lèi)型的基礎(chǔ)類(lèi)型參數(shù)。 public static Type GetUnderlyingType(Type nullableType); //比較兩個(gè)相對(duì)值 System.Nullable對(duì)象。 public static int Compare (T? n1, T? n2) where T : struct //指示兩個(gè)指定 System.Nullable 對(duì)象是否相等。 public static bool Equals (T? n1, T? n2) where T : struct }
在這里面我們重點(diǎn)說(shuō)明一下GetUnderlyingType(Type nullableType)
方法,另外兩個(gè)方法是用來(lái)比較值的,大家可以自己研究。
GetUnderlyingType(Type nullableType)
方法是用來(lái)返回一個(gè)可為空類(lèi)型的基礎(chǔ)類(lèi)型,如果?nullableType
?參數(shù)不是一個(gè)封閉的Nullablenull
。?
Console.WriteLine(Nullable.GetUnderlyingType(typeof(Nullable))); //輸出結(jié)果:System.Int32 Console.WriteLine(Nullable.GetUnderlyingType(typeof(Nullable<>)) == null); //輸出結(jié)果:True Console.WriteLine(Nullable.GetUnderlyingType(typeof(int)) == null); //輸出結(jié)果:True Console.WriteLine(Nullable.GetUnderlyingType(typeof(string)) == null); //輸出結(jié)果:True
八、語(yǔ)法糖
微軟對(duì)Nullable
簡(jiǎn)寫(xiě)
int? i = 5; int? j = null; var r = i != null; var v = (int) i; i++; i = i + 3; r = i != j; r = i >= j; var k = i + j; double? x = 5; double? y = j;
編譯后的語(yǔ)句
int? i = new int?(5); int? j = new int?(); var r = i.HasValue; var v = i.Value; i = i.HasValue ? new int?(i.GetValueOrDefault() + 1) : new int?(); i = i.HasValue ? new int?(i.GetValueOrDefault() + 3) : new int?(); r = i.GetValueOrDefault() != j.GetValueOrDefault() || i.HasValue != j.HasValue; r = i.GetValueOrDefault() >= j.GetValueOrDefault() && i.HasValue & j.HasValue; int? k = i.HasValue & j.HasValue ? new int?(i.GetValueOrDefault() + j.GetValueOrDefault()) : new int?(); double? x = new double?((double) 5); double? y = j.HasValue ? new double?((double) j.GetValueOrDefault()) : new double?();
原文鏈接:https://www.cnblogs.com/tdfblog/p/Nullable-Types-in-Csharp-Net.html
相關(guān)推薦
- 2022-10-16 Android面向切面基于A(yíng)OP實(shí)現(xiàn)登錄攔截的場(chǎng)景示例_Android
- 2023-07-04 Linux直接創(chuàng)建SSH無(wú)密碼連接
- 2022-07-13 RedisDesktopManager遠(yuǎn)程連接redis的實(shí)現(xiàn)_Redis
- 2022-04-20 Python設(shè)計(jì)模式結(jié)構(gòu)型組合模式_python
- 2022-08-20 Oracle刪除歸檔日志及添加定時(shí)任務(wù)_oracle
- 2022-05-12 el-table滾動(dòng)懶加載,顯示加載狀態(tài)
- 2023-06-03 redis中刪除操作命令_Redis
- 2022-09-02 基于Python實(shí)現(xiàn)配置熱加載的方法詳解_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支