C#中Nullable<T>
C#中Nullable<T>一、Nullable<T>在C#中的定义
namespace System
{
[Serializable]
public struct Nullable<T> where 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>(T value) {
return new Nullable<T>(value);
}
public static explicit operator T(Nullable<T> value) {
return value.Value;
}
}
}
二、Nullable<T>类型的说明
1、Nullable<T> 类型也是一个值类型;
2、Nullable<T> 类型包含一个Value属性用于表示基础值,还包括一个Boolean类型的HasValue属性用于表示该值是否为null ;
3、Nullable<T> 是一个轻量级的值类型。Nullable<T>类型的实例占用内存的大小等于一个值类型与一个Boolean类型占用内存大小之和;
4、Nullable<T> 的泛型参数T必须是值类型。您只能将Nullable<T>类型与值类型结合使用,您也可以使用用户定义的值类型。
5、可空类型在基元类型正常值范围外加上了空值null。
6、使用可空类型,接收数据库的可空字段值时更加方便。
三、Nullable<T>的简单使用实例
Nullable<int> i = 1; //简写为:int? i = 1;
Nullable<int> j = null;
Console.WriteLine(i.HasValue);
//输出结果:True
Console.WriteLine(i.Value);
//输出结果:1
Console.WriteLine(j.HasValue);
//输出结果:False
Console.WriteLine(j.Value);
//抛异常: System.InvalidOperationException
备注
1、Nullable<T>的简写形式为:T?
2、可以通过 Value 属性来获取基础类型的值。如果不为null,则将返回实际的值,否则将抛出InvalidOperationException异常;所以在调用Value属性的时,需要检查是否为null。
四、Nullable<T>类型的转换和运算
1、Nullable<T>类型转换
// 从System.Int32隐式转换为Nullable<Int32>
int? i = 5;
// 从'null'隐式转换为Nullable<Int32>
int? j = null;
// 从Nullable<Int32>到Int32的显式转换
int k = (int)i;
// 基础类型之间的转换
Double? x = 5; // 从Int到Nullable<Double> 的隐式转换
Double? y = j; // 从Nullable<Int32> 隐式转换Nullable<Double>
2、Nullable<T>类型运算
对Nullable<T> 类型使用操作符,与包含的基础类型使用方法相同。
(1)、一元运算符(++、--、 - 等),如果Nullable<T>类型值是null时,返回null;
(2)、二元运算符(+、-、*、/、%、^等)任何操作数是null,返回null;
(3)、对于==运算符,如果两个操作数都是null,则表达式计算结果为true,如果任何一个操作数是null,则表达式计算结果为false;如果两者都不为null,它照常比较。
(4)、对于关系运算符(>、<、>=、<=),如果任何一个操作数是null,则运算结果是false,如果操作数都不为null,则比较该值。
int? i = 5;
int? j = null;
// 一元运算符
i++; // i = 6
j = -j; // j = null
// 二元运算符
i = i + 3; // i = 9
j = j * 3; // j = null;
// 等号运算符(==、!=)
var r = i == null; //r = false
r = j == null; //r = true
r = i != j; //r = true
// 比较运算符(<、>、<=、>=)
r = i > j; //r = false
i = null;
r = i >= j; //r = false,注意,i=null、j=null,但是>=返回的结果是false
五、Nullable<T>的装箱与拆箱
1、当一个Nullable<T>类型的实例装箱时,CLR会检查实例的HasValue属性:如果是true,则将实例Value属性的值进行装箱后返回结果;如果返回false,则直接返回null,不做任何的处理。
2、在拆箱处理时,与装箱处反。CLR会检查拆箱的对象是否为null,如果是直接创建一个新的实例 new Nullable<T>(),如果不为null,则将对象拆箱为类型T,然后创建一个新实例 new Nullable<T>(T)。
int? n = null;
object o = n; //不会进行装箱操作,直接返回null值
Console.WriteLine("o is null = {0}", object.ReferenceEquals(o, null));
//输出结果:o is null = True
n = 5;
o = n; //o引用一个已装箱的Int32
Console.WriteLine("o's type = {0}", o.GetType());
//输出结果:o's type = System.Int32
o = 5;
//将Int32类型拆箱为Nullable<Int32>类型
int? a = (Int32?)o; // a = 5
//将Int32类型拆箱为Int32类型
int b = (Int32)o; // b = 5
// 创建一个初始化为null
o = null;
// 将null变为Nullable<Int32>类型
a = (Int32?)o; // a = null
b = (Int32)o; // 抛出异常:NullReferenceException
六、Nullable<T>的ToString()方法
当调用Nullable<T>类型的ToString()方法时,如果HasValue属性的值为false,则返回String.Empty,如果该属性的值为true,则调用的逻辑是Value.ToString()。
int? i = 10;
Console.WriteLine(i.ToString());
//输出结果:10
i = null;
Console.WriteLine(i.ToString() == string.Empty);
//输出结果:True