没有值的时候怎么办?
对于一个引用类型的变量来说,其值是一个引用。而值类型变量的值是真正的值本身。对于引用类型来说,null是一个特殊的值,它表示“该引用变量没有引用任何对象”。但是,它并不是一个有效的值类型的值。这非常容易理解,因为如果C#设计者将null设计为值类型的有效位,那么势必将增加内存开销。
在C#1中,表示空值的值类型变量往往有如下几种模式:
1、魔值,也就是说,牺牲一个值来表示空值。
2、引用类型包装,它有两种类型:直接利用object作为变量类型,并根据需要进行装箱和拆箱操作;对于一个可空值类型,为其准备一个引用类型。
3、额外的bool标记
System,Nullable<T>:
该类型是可空类型的核心结构。它是一个泛型类型,且类型参数T存在一个值类型约束,T称为可空类型的基础类型。它最重要的两个属性是HasValue和Value,前者是一个bool类型,它指出是否存在一个真正的值,后者则描述一个真正的值。
成员函数的调用:
using System;
using System.Collections.Generic;
using System.Text;
namespace CS_TEST
{
class Program
{
static void Display(Nullable<int> x)
{
Console.WriteLine("HasValue:{0}", x.HasValue);
if (x.HasValue)
{
Console.WriteLine("Value:{0}", x.Value);
Console.WriteLine("Explicit conversion:{0}", (int)x);
}
Console.WriteLine("GetValueOrDefault():{0}", x.GetValueOrDefault());
Console.WriteLine("GetValueOrDefault(10):{0}", x.GetValueOrDefault(10));
Console.WriteLine("ToString():\"{0}\"", x.ToString());
Console.WriteLine("GetHashCode();{0}", x.GetHashCode());
Console.WriteLine();
}
static void Main(string[] args)
{
Nullable<int> x = 5;
x = new Nullable<int>(5);
Console.WriteLine("Instance with value:");
Display(x);
Nullable<int> y = new Nullable<int>();
Console.WriteLine("Instance with value:");
Display(y);
Console.ReadLine();
}
}
}
Nullable:
这个类可以辅助泛型可空类型做一些事情,它只包含静态方法,不能创建实例。
3个静态方法:
public static int Compare<T>(Nullable<T> n1, Nullable<T> n2)public static bool Equals<T>(Nullable<T> n1, Nullable<T> n2)
第三个方法不是泛型方法:
public static Type GetUnderlyingType<Type nullbaleType>
如果第三个方法的参数是一个可空类型,那么返回它的基础类型,否则返回null。
C#2提供给可空类型的语法:
?修饰符:T? 等价于 Nullable<T>,用法完全一样,只是语法不同,举例如下:
int? nullable = 5;//定义可空类型
object boxed = nullable;//装箱
Console.WriteLine(nullable.GetType());
int normal = (int)boxed;//拆箱成基础类型
Console.WriteLine(normal);
nullable = (int?)boxed;//拆箱成可空类型
Console.WriteLine(nullable);
nullable = new int?();//将可空类型变量初始化为空值
boxed = nullable;//装箱
Console.WriteLine(boxed==null);
nullable = (int?)boxed;//拆箱成可空类型
Console.WriteLine(nullable.HasValue);
//将空值可空类型拆箱成基础类型,将抛出异常
normal = (int)boxed;//拆箱成基础类型
Console.WriteLine(normal);
使用null进行赋值和比较:
C#允许使用null在比较和赋值中表示一个可空类型的空值,就跟引用类型一样。
值得注意的一个地方:
DataTime? death;
DataTime birth;
求寿命的方法:
public TimeSpan Age
{
get
{
if (death == null) return DataTime.Now - birth;
return death.Value - birth;
}
}
此外,null可以作为参数进行传递,代表一个空值可空类型变量
思考:为什么在最后一行代码中要使用death.Value - birth而非death - birth?
可空类型转换:
null 到 T?的隐式转换
T 到 T?的隐式转换
T?到 T 的显式转换
S? 到 T?的转换(显式隐式皆可)
S 到 T?的转换
S?到 T的显示转换
需要注意的是,如果S?是空值,那么S?-->T的显示类型转换会抛出一个异常。
可空类型操作符的提升:略
提几个比较值得注意的地方:
对于比较操作符,(null == null) == true,其它含有null的比较均为false;
对于一元或者二元运算,如果有一个操作数为null,那么运算的结果也是一个null,即空值。
可空逻辑:
bool? a, b;
此时可空布尔类型有三种值,true, false, null。
此时布尔表达式的值取决于,如果空值的取值对布尔表达式的值有影响,那么布尔表达式的值为null。布尔表达式的值为确定的布尔表达式的值。
空接合操作符:??
对于表达式:first??second
(1) 求first的值
(2) 如果first的值非空,那么first的值就为表达式的值
(3) 否则表达式的值为second的值
因此,如前所述的代码:
if (death == null) return DataTime.Now - birth;
return death.Value - birth;
可以写成:
return (death??DataTime.Now) - birth;
现在回答一下前面红色标注的问题:
若使用death - birth,其结果是一个DataTime?,因为从T到T?存在一个隐式类型转换,而反过来的转换泽必定是显式的。直接使用death肯定是可以的,但是需要作者对返回的结果再进行处理。