可空类型

     通常一个值类型变量永远不可能为null,它总是包含值类型的值本身。但是在某些情况下会遇到一些问题如:在映射数据库中的一个C#可空值列时,使用Framework来处理数据库数据时变得相当困难;为了解决这一问题CLR中引入了“C#可空值类型(nullable value type)”

    为理解它们是如何工作的,先看看CLR中的逻辑:

  1. [Serializable, StructLayout(LayoutKind.Sequential)]     
  2.  public struct Nullable< T> where T : struct    
  3.  {     
  4.      private Boolean hasValue = false// 用一个字段落表示装态,初始假定为Null     
  5.      internal T value = default(T);     
  6.      public Nullable(T value)     
  7.      {     
  8.          this.value = value;     
  9.          this.hasValue = true;     
  10.      }     
  11.     
  12.      public bool HasValue { get { return hasValue; } }     
  13.      public T Value     
  14.      {     
  15.          get    
  16.          {     
  17.              if (!hasValue) throw new InvalidOperationException("Nullable object must have a value.");     
  18.              return value;     
  19.          }     
  20.      }     
  21.     
  22.      public T GetValueOrDefault() { return value; }     
  23.      public T GetValueOrDefault(T defaultValue)     
  24.      {     
  25.          if (!HasValue) return defaultValue;     
  26.          return value;     
  27.      }     
  28.      public override bool Equals(object obj)     
  29.      {     
  30.          if (!HasValue) return (obj == null);     
  31.          if (obj == nullreturn false;     
  32.          return value.Equals(obj);     
  33.      }     
  34.     
  35.      public override int GetHashCode()     
  36.      {     
  37.          if (!HasValue) return 0;     
  38.          return value.GetHashCode();     
  39.      }     
  40.      public override string ToString()     
  41.      {     
  42.          if (!HasValue) return String.Empty;     
  43.          return value.ToString();     
  44.      }     
  45.     
  46.      public static implicit operator Nullable< T>(T value)     
  47.      {     
  48.          return new Nullable< T>(value);     
  49.     
  50.      }     
  51.     
  52.  }   

    调用和输出: 

  1.    static void Main(string[] args)     
  2.         {     
  3.             Nullable< Int32> x = 5;     
  4.             Nullable< Int32> y = null;     
  5.             Console.WriteLine("x:HasValue {0}, value = {1}", x.HasValue, x.Value);     
  6.             Console.WriteLine("y:HasValue {0}, value = {1}"y.HasValue, y.GetValueOrDefault());    
  7.             // 虽然可空类型变量y为null,但是还是可以执行成员方法。
  8.             // y.ToString() 方法返回空值。
  9.             // 虽然y为null,但是实际的可空变量已经构造了,所以可以调用可空类型的方法和属性。
  10.             // 如果引用类型为null,那么引用类型变量没有构造,不能调用任何引用类型的方法和属性。
  11.             Console.ReadLine();     
  12.         }     
  13. 输出:     
  14.     x:HasValue  True, value = 5     
  15.     y:HasValue False, value = 0    

    C#中允许使用问号来申明初始化变量(等同于上面代码)如:

    Int32? x = 5;

    Int32? y = null;

    总结一下C#可空值类型对操作符的解释:

    a.一元操作符如果操作数为null,结果为null;

    b.二元操作符中任何一个为null,结果为null;

    c.比较操作符如果两人个操作数都为null,两者相等;如果一个为null,两者不相等;如果两个数都不为null,对值进行比较;

    注意:在操作值类型时会生成大量代码,代码类似对基类(Nullable)代码的调用;

  1. //一元操作符:(+ ++ - -- ! ~)     
  2. x++; // x = 6;     
  3. y--; // y = null;     
  4. //二元操作符:(+ - * / % & | ^ < <  >>)     
  5. x = x + 10; // x = 15;     
  6. y = y * 10; // y = null     
  7. // 比较操作符:(== != <  > < = >=)     
  8. if (x == null) Console.WriteLine("x is null;"); else Console.WriteLine("x is not null;");     
  9. if (y == null) Console.WriteLine("y is null;"); else Console.WriteLine("y is not null;");     
  10. if (x != y) Console.WriteLine("x = y;"); else Console.WriteLine("x != y;");     
  11. if (x > y) Console.WriteLine("x > y;"); else Console.WriteLine("x < = y;");    

    当CLR对一个Nullable< T>实例进行装箱时,会检查它是否为null,如果为null,CLR不实际进行装箱操作,并会返回null值;

    如果实例不为空,将从类型中取出值,并对其进行装箱如:

  1. Int32? a = null;     
  2.             object o = a; //a 为null     
  3.             Console.WriteLine(" o is null = {0}", o == null); // "true"     
  4.     
  5.             Int32? b = 5;     
  6.             o = b; //a 为null     
  7.             Console.WriteLine(" o's type = {0}", o.GetType()); // "System.Int32" 对可空值类型调用GetType时CLR会采取欺骗手法返回T,而不是Nullable< T>   

    在应用可空值类型进行拆箱时,CLR会分配内存(这是一个极特殊的行为,在其它所有情况下,拆箱永远不会导致内存的分配),原因在于一个已装箱的值类型不能简单的拆箱为值类型的可空版本,在已装箱的值类型中并不包含Boolean hasValue字段,故在拆箱时CLR必须分配一个Nullable< T>对象,已初始化hasValue = true ,value = 值类型值。这会对应用程序性能造成一定影响。

posted on 2012-07-19 17:28  zlwzlwzlw  阅读(150)  评论(0编辑  收藏  举报

导航