可空类型的应用范围
可空类型是为.NET中的值类型设计的,可空类型只能引用值类型变量,不能引用引用类型变量。
可空类型最常用的是在数据库中,有些整形字段可能不是必填项,这时从数据库中取记录,对于这个为空的整形字段就要非常小心了。而有了可空类型后,就可以很简单的表示。
int age;
if(PersonInfoRow.IsAgeNull()==false)
{age=int.pravse(personInfoRow.Age.tostring());
}
else
{
age=-1; // 字段为空,需要为变量赋默认值
}
使用可空类型要注意事项
(1)因为可空类型可能为空,于是很多运算法则将不再简单适用,比如A+B,如果A或者B当中有一个或者两个为可空类型,那么它们便不能简单相加,因为它们可能会为空,这时结果也就可能会为空,所以一定要先使用HasValue属性测试是否为空,或者使用GetValueOrDefault属性返回该基础类型所赋的值或默认值,再或者使用??运算符分配默认值,当前值为空的可空类型被赋值给非空类型时将应用该默认值,比如int?x=null;int y=x??-1;,但是切不可直接进行运算。
(2)可空类型可以强制转换为对应的基础类型,比如int?x=3;int y=(int)x;,但在转换之前一定要先测试是否不为空,如果为空的话就会出错。
(3)由于以上两点原因,建议不要随意使用可空类型,只在必要的时候才使用,使用时一定要小心谨慎,随时记得判断是否为空。
可空类型的实现
Nullable <T> 这个泛型类:
[Serializable, StructLayout(LayoutKind.Sequential), TypeDependency("System.Collections.Generic.NullableComparer`1"), TypeDependency("System.Collections.Generic.NullableEqualityComparer`1")]
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 this.hasValue;
}
}
public T Value
{
get
{
if (!this.HasValue)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue);
}
return this.value;
}
}
public T GetValueOrDefault()
{
return this.value;
}
public T GetValueOrDefault(T defaultValue)
{
if (!this.HasValue)
{
return defaultValue;
}
return this.value;
}
public override bool Equals(object other)
{
if (!this.HasValue)
{
return (other == null);
}
if (other == null)
{
return false;
}
return this.value.Equals(other);
}
public override int GetHashCode()
{
if (!this.HasValue)
{
return 0;
}
return this.value.GetHashCode();
}
public override string ToString()
{
if (!this.HasValue)
{
return "";
}
return this.value.ToString();
}
public static implicit operator T?(T value)
{
return new T?(value);
}
public static explicit operator T(T? value)
{
return value.Value;
}
}
可以看到,可空类型为null时,直接使用Value属性时,会产生异常;直接使用ToString方法输出空字符串。这一点和引用类型不一样,引用类型为null时,使用ToString会异常。
int ? m = null;
m.ToString(); // 结果为空字符串
string s = null;
s.ToString(); // 异常
从这里可以看出,给可空类型变量赋空值时,会调用可空类型的构造函数,实例化一个对象,只不过对象没有指定其内部值。所以此时调用ToString方法是合法的。
而给引用类型赋空值时,不会调用构造函数,没有实例产生,当然不能调用实例方法。
可空类型从本质上来讲是属于值类型,所以同样可以进行装箱和拆箱操作。
int? notnullable = 5;
int ? nullable = null;
object obj1 = notnullable; // 装箱操作
notnullable = (int?)obj1; // 可以再进行拆箱操作
int m = (int)obj1; // 可以直接拆箱成内部类型
object obj2 = nullable; // 装箱操作,由于可空类型为空,所以obj2为空
nullable = (int?)obj2; // 可以拆箱成可空类型
m = (int)obj2; // 异常, 因为为空,所以不能拆箱成内部值类型