国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

[CLR via C#]19. 可空值類型

2019-11-17 03:19:10
字體:
來源:轉載
供稿:網友

[CLR via C#]19. 可空值類型

  我們知道,一個值類型的變量永遠不可能為null。它總是包含值類型本身。遺憾的是,這在某些情況下會成為問題。例如,設計一個數據庫時,可將一個列定義成為一個32位的整數,并映射到FCL的Int32數據類型。但是,數據庫中的一個列可能允許值為空;用Microsoft .NET Framework處理數據庫可能變得相當困難,因為在CLR中,沒有辦法將一個Int32值表示為null。   Microsoft ADO.NET的表適配器確實支持可空類型。但遺憾的是,System.Data.SqlType命名空間中的值類型沒有用可空類型替換,部分原因是類型之間沒有"一對一"的對應關系。例如,SqlDecimal類型對最大允許38位數,而普通的Decimal類型最大只允許29位數。   還有一個例子:在java中,java.util.Date類是一個引用類型,所以該類型的一個變量能設為null。但在CLR中,System.DateTime是一個值類型,一個DateTime變量永遠都不能設為null。如果用java寫的一個應用程序想和運行CLR的一個web服務交流日期/時間,那么一旦java發送了一個null,就會出問題,因為CLR不知道如何表示null,不知道如何操作它。   為了解決這個問題,Microsoft在CLR中引入了可空值類型(nullable value type)的概念。為了理解它們是如何工作的,先看一看System.Nullable<T>類。他是在FCL中定義的。以下是System.Nullable<T>類型定義的邏輯表示:

[Serializable][DebuggerStepThrough]public struct Nullable<T> where T : struct{  #region Sync with runtime code  //下面兩個字段表示狀態  internal T value;  internal bool has_value;  #endregion  public Nullable(T value)  {    this.has_value = true;    this.value = value;  }   public bool HasValue  {    get { return has_value; }  }  public T Value  {   get    {    if (!has_value)    throw new InvalidOperationException("Nullable object must have a value.");    return value;    }  }  public override bool Equals(object other)  {    if (other == null)    return has_value == false;    if (!(other is Nullable<T>))    return false;    return Equals((Nullable<T>)other);  }  bool Equals(Nullable<T> other)  {    if (other.has_value != has_value)    return false;    if (has_value == false)    return true;    return other.value.Equals(value);  }  public override int GetHashCode()  {    if (!has_value)    return 0;    return value.GetHashCode();  }  public T GetValueOrDefault()  {    return value;  }  public T GetValueOrDefault(T defaultValue)  {    return has_value ? value : defaultValue;  }  public override string ToString()  {    if (has_value)    return value.ToString();    else    return String.Empty;  }  public static implicit operator Nullable<T>(T value)  {    return new Nullable<T>(value);  }  public static explicit operator T(Nullable<T> value)  {    return value.Value;  }  //  // These are called by the JIT  //  #PRagma warning disable 169  //  // JIT implementation of box valuetype System.Nullable`1<T>  //  static object Box(T? o)  {    if (!o.has_value)    return null;    return o.value;  }  static T? Unbox(object o)  {    if (o == null)    return null;    return (T)o;  }  #pragma warning restore 169}

  可以看出,這個類封裝了也可以為null的一個值類型的表示。由于Nullable<T>本身是一個值類型,所以它的實例仍然是"輕量級"的。也就是說,實例仍然在棧上,而且一個實例的大小就是原始值類型的大小加上一個Boolean字段的大小。注意,Nullable的類型參數T被約束為struct。這是由于引用類型的變量已經可以為null,所以沒必要再去照顧它。.

  現在,如果想要在代碼中使用一個可空的Int32,就可以向下面這樣寫:

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());

  輸出的結果為:

x: HasValue=True, Value=5y: HasValue=False, Value=0   一、C#對可空值類型的支持   注意,C#允許在代碼中使用簡單的語法來初始化上述兩個Nullable<Int32>變量x和y。事實上,C#開發團隊希望將可空值類型集成到C#語言中,是它們成為"一等公民"。為此,C#提供了一個更清晰的語法來處理可空值類型。C#允許用問號表示法來聲明并初始化x和y變量:

Int32? x =5;Int32? y =null;

  在C#中,Int32等價于Nullable<Int32>。但是,C#在此基礎上更進一步,允許開發人員在可空實例上進行轉換和轉型。C#還允許開發人員向可空實例類型應用操作符。以下代碼對此進行了演示;

private static void ConversionsAndCasting(){    // 從可空的 Int32 轉換為 Nullable<Int32>    Int32? a = 5;    // 從'null'隱式轉換為 Nullable<Int32>    Int32? b = null;    // 從 Nullable<Int32> 顯示轉換為 Int32    Int32 c = (Int32)a;    // 在可空基類型之間的轉型    Double? d = 5; // Int32 轉型到 Double? (d是double類型 值為5)    Double? e = b; // Int32? 轉型到 Double? (e為 null)}

  C#還允許向可空實例應用操作符。下面是一些例子:

private static void Operators(){    Int32? a = 5;    Int32? b = null;    // 一元操作符 (+ ++ - -- ! ~)    a++; // a = 6    b = -b; // b = null    // 二元操作符 (+ - * / % & | ^ << >>)    a = a + 3; // a = 9    b = b * 3; // b = null;    // 相等性操作符 (== !=)    if (a == null) { /* no */ } else { /* yes */ }    if (b == null) { /* yes */ } else { /* no */ }    if (a != b) { /* yes */ } else { /* no */ }    // 比較操作符 (<, >, <=, >=)    if (a < b) { /* no */ } else { /* yes */ }}

  下面總結了C#如何解析操作符:

  * 一元操作符 操作符是null,結果也是null   * 二元操作符 兩個操作符中任何一個是null,結果就是null。但有一個例外,它發生在將&和|操作符應用于Boolean?操作數的時候。在這種情況下,這兩個操作符的行為和SQL的三值邏輯一樣的。對于這兩個操作符,如果兩個操作符都不是null,那么操作符和平常一樣工作。如果兩個操作符都是null,結果就是null。特殊情況就是其中之一為null時發生。下面列出了針對操作符的各種true,false和null組合:

操作符→truefalsenull
操作符↓
true& = true| = true& = false| = true& = null| = true
false

& = false

| = true

& = false| = false& = false| = null
null& = null| = true& = false| = null& = null| = null

  * 相等性操作 兩個操作符都是null,兩者相等。一個操作符為null,則兩個不相等。兩個操作數都不是null,就比較值來判斷是否相等。  * 關系操作符 兩個操作符任何一個是null,結果就是false。兩個操作數都不是null,就比較值。

應該注意的是,操作符實例時會生成大量代碼。例如以下方法:

private static Int32? NullableCodeSize(Int32? a, Int32? b) {  return (a + b);}

  在編譯這個方法時,會生成相當多的IL代碼,而且會使對可空類型的操作符慢于非可控類型執行的同樣的操作。編譯器生成的代碼等價于以下C#代碼:

private static Nullable<Int32> NullableCodeSize(    Nullable<Int32> a, Nullable<Int32> b) {    Nullable<Int32> nullable1 = a;    Nullable<Int32> nullable2 = b;    if (!(nullable1.HasValue & nullable2.HasValue)){    return new Nullable<Int32>();}    return new Nullable<Int32>(nullable1.GetValueOrDefault() + nullable2.GetValueOrDefault());}

  19.2 C#的空結合操作符  C#提供了一個所謂的"空結合操作符",即??操作符,它要獲取兩個操作符。假如左邊的操作符不為null,就返回操作符這個操作符的值。如果左邊的操作符為null,就返回右邊的操作符的值。利用空接合操作符,可方便地設置的默認值。   空接合操作符的一個妙處在于,它既能用于引用類型也能用于可空值類型。以下代碼演示了如何使用??操作符:

private static void NullCoalescingOperator() {    Int32? b = null;    // 下面這行等價于:    // x = (b.HasValue) ? b.Value : 123    Int32 x = b ?? 123;     Console.WriteLine(x); // "123"    // 下面這行等價于:    // String temp = GetFilename();    // filename = (temp != null) ? temp : "Untitled";    String filename = GetFilename() ?? "Untitled";}

  有人爭辯說??操作符不過是?:操作符的"語法糖"而已,所以C#團隊不應該將這個操作符添加到語言中。實際上,??提供了重大的語法上的改進。

  第一個改進是??操作符能更好的支持表達式:

Func<String> f = () => SomeMethod ?? "Untitled";

  相比下一行代碼,上述代碼更容易容易閱讀和理解。下面這行代碼要求進行變量賦值,而且用一個語句還搞不定:

Func<String> f = () => { var temp = SomeMethod(); return temp !=null ?temp : "Untitled"; }

  第二個改進就是??在符合情形下更好用。例如,下面這行代碼:

String s = SomeMethod() ?? SomeMethod2 ?? "Untitled";

  它比下面這一堆代碼更容易理解和閱讀:

String s;var sm1 = SomeMethod();if (sm1 != null) s = sm1;else {var sm2 = SomeMethod2();if (sm2 !=null) s = sm2;elses = "Untitled";}

  三、CLR對可空值類型的特殊支持   1.可空值類型的裝箱   先假定有一個為null的Nullable<Int32>變量。如果將該變量傳給一個期待獲取一個Object的方法,那么該變量必須裝箱,并將對已裝箱的Nullable<Int32>的一個引用傳給方法。但是,這在邏輯上講不通,因為現在向方法傳遞的一個非null的值——而Nullable<Int32>變量邏輯上包含的是null值。為了解決這個問題,CLR會在裝箱一個可空變量時執行一些特殊代碼。   具體地說,當CLR對一個Nullable<T>實例進行裝箱時,會檢查它是否為null。如果是CLR不實際裝箱任何內容,并返回null。如果可空類型實例不為null,CLR從可空實例中取出值,并對其進行裝箱。也就是說,一個值為5的Nullable<Int32>會裝箱成為值為5的一個已裝箱的Int32。以下代碼對這一行進行了演示:

private static void Boxing() {    // 對Nullable<T>進行裝箱,要么返回null,要么返回一個已裝箱的T    Int32? n = null;    Object o = n; // o 為 null    Console.WriteLine("o is null={0}", o == null); // "True"    n = 5;    o = n; // o 引用一個已裝箱的Int32    Console.WriteLine("o's type={0}", o.GetType()); //     "System.Int32"}

  其實在第一節中的Nullable<T>源碼中已有顯示,如:

static object Box(T? o){    if (!o.has_value)    return null;    return o.value;}

  2. 可空值類型的拆箱   CLR允許將一個已裝箱的值類型T拆箱為一個T或者一個Nullable<T>。如果對已裝箱值類型的引用是null,而且要把它拆箱為一個Nullable<T>,那么CLR會將Nullable<T>的值設為null。以下代碼進行了演示:

private static void Unboxing() {    // 創建一個已裝箱的In
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 沁阳市| 常山县| 澳门| 泰州市| 明溪县| 安徽省| 恭城| 五台县| 桂阳县| 麻栗坡县| 卢龙县| 偏关县| 荔浦县| 保康县| 黄陵县| 曲靖市| 临泉县| 吉木萨尔县| 平山县| 漳浦县| 康定县| 墨玉县| 尚义县| 长春市| 邓州市| 资阳市| 成都市| 武胜县| 清丰县| 五河县| 沙湾县| 普格县| 苏尼特右旗| 上杭县| 台中县| 凤阳县| 隆尧县| 洪洞县| 松潘县| 铜山县| 澎湖县|